Skip to content

Commit db48161

Browse files
authored
feat(decopilot): enhance model resolution and agent bindings (#2823)
* feat(decopilot): enhance model resolution and agent bindings - Added a new function `resolveDefaultModels` to handle default model resolution for organizations. - Updated the `createDecopilotRoutes` to utilize the new model resolution logic, allowing fallback to organization defaults if client models are not provided. - Made `models` in `StreamRequestSchema` optional to accommodate scenarios where models may not be specified. - Introduced agent binding configurations and a new `streamAgent` function for improved agent interactions. - Enhanced the `streamCoreInner` function to ensure all messages have an ID, improving message handling consistency. - Updated package dependencies to include the new `ai` package version. This commit lays the groundwork for more robust AI model handling and agent interactions within the Decopilot framework. * feat(runtime): enhance AgentModelInfoSchema and refactor isAgent function - Added `.passthrough()` to `AgentModelInfoSchema` to allow additional properties. - Refactored `isAgent` function to be a constant instead of an exported function, improving encapsulation. These changes improve the flexibility of the agent model schema and streamline the type-checking function. * chore(runtime): remove optional peer dependency metadata for 'ai' - Deleted the optional peer dependency metadata for the 'ai' package in package.json, simplifying the dependency structure. This change streamlines the package configuration by removing unnecessary metadata. * feat(docs): add agent bindings documentation in English and Portuguese - Introduced new documentation files for agent bindings in both English and Portuguese, detailing how to connect AI agents to MCP Apps and stream responses programmatically. - Updated navigation to include links to the new agent bindings documentation. This addition enhances the accessibility of information regarding agent bindings for a broader audience.
1 parent f1361c9 commit db48161

11 files changed

Lines changed: 819 additions & 18 deletions

File tree

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
---
2+
title: Agent Bindings
3+
description: Bind AI agents to your MCP App and stream responses programmatically
4+
icon: Bot
5+
---
6+
import Callout from "../../../../components/ui/Callout.astro";
7+
8+
## Overview
9+
10+
Agent bindings let your MCP App call [decopilot agents](/en/mcp-mesh/agents) directly from tool handlers. This enables tools that delegate work to AI agents — for example, a customer support tool that streams an agent's reasoning back to the user.
11+
12+
Agent bindings work like [connection bindings](/en/full-code-guides/building-tools#using-integrations) (`BindingOf`), but instead of calling MCP tools on a connection, they stream messages to and from a decopilot agent.
13+
14+
## Declaring an Agent Binding
15+
16+
Use `AgentOf()` in your `StateSchema` to declare an agent binding:
17+
18+
```typescript
19+
// api/types/env.ts
20+
import type { DefaultEnv } from "@decocms/runtime";
21+
import { AgentOf } from "@decocms/runtime";
22+
import { z } from "zod";
23+
24+
export const StateSchema = z.object({
25+
MY_AGENT: AgentOf(),
26+
});
27+
28+
export type Env = DefaultEnv<typeof StateSchema>;
29+
```
30+
31+
When someone installs your MCP App, the admin UI will show a picker for the `MY_AGENT` field where they can select an agent from the organization.
32+
33+
At runtime, `MY_AGENT` resolves to a client with a `STREAM` method — you never deal with HTTP or SSE parsing directly.
34+
35+
## Using the Agent in a Tool
36+
37+
Once declared, the resolved agent is available on `env.MY_AGENT`:
38+
39+
```typescript
40+
import { createTool } from "@decocms/runtime/tools";
41+
import { z } from "zod";
42+
import type { Env } from "../types/env.ts";
43+
44+
export const askAgentTool = (env: Env) =>
45+
createTool({
46+
id: "ask_agent",
47+
description: "Ask the configured agent a question and stream the response",
48+
inputSchema: z.object({
49+
question: z.string().describe("The question to ask"),
50+
}),
51+
outputSchema: z.object({
52+
answer: z.string(),
53+
}),
54+
execute: async ({ context }) => {
55+
const stream = await env.MY_AGENT.STREAM({
56+
messages: [{ role: "user", parts: [{ type: "text", text: context.question }] }],
57+
});
58+
59+
let answer = "";
60+
for await (const message of stream) {
61+
// Each message is a UIMessage from the AI SDK
62+
for (const part of message.parts) {
63+
if (part.type === "text") {
64+
answer += part.text;
65+
}
66+
}
67+
}
68+
69+
return { answer };
70+
},
71+
});
72+
```
73+
74+
## STREAM Parameters
75+
76+
The `STREAM` method accepts the following parameters:
77+
78+
```typescript
79+
interface AgentStreamParams {
80+
/** Messages to send to the agent (required) */
81+
messages: Omit<UIMessage, "id">[];
82+
83+
/** Override the credential used for model access */
84+
credentialId?: string;
85+
86+
/** Override model configurations */
87+
thinking?: AgentModelInfo;
88+
coding?: AgentModelInfo;
89+
fast?: AgentModelInfo;
90+
91+
/** Tool approval level: "auto" | "readonly" | "plan" */
92+
toolApprovalLevel?: "auto" | "readonly" | "plan";
93+
94+
/** Sampling temperature */
95+
temperature?: number;
96+
97+
/** Memory configuration for conversation continuity */
98+
memory?: { windowSize: number; thread_id: string };
99+
100+
/** Thread ID for multi-turn conversations */
101+
thread_id?: string;
102+
}
103+
```
104+
105+
The model parameters (`thinking`, `coding`, `fast`), `credentialId`, `toolApprovalLevel`, and `temperature` all have defaults from the binding configuration set at install time. Parameters passed to `STREAM` override those defaults.
106+
107+
### Model Tiers
108+
109+
Agents can use up to three model tiers:
110+
111+
| Tier | Purpose |
112+
|------|---------|
113+
| `thinking` | Primary model for reasoning and complex tasks |
114+
| `coding` | Optimized for code generation (falls back to thinking if not set) |
115+
| `fast` | Lightweight model for quick, simple responses |
116+
117+
Each tier is an `AgentModelInfo` object:
118+
119+
```typescript
120+
interface AgentModelInfo {
121+
id: string; // Model identifier (e.g. "claude-sonnet-4-6")
122+
title: string; // Display name
123+
capabilities?: {
124+
vision?: boolean;
125+
text?: boolean;
126+
tools?: boolean;
127+
reasoning?: boolean;
128+
};
129+
provider?: string | null;
130+
limits?: {
131+
contextWindow?: number;
132+
maxOutputTokens?: number;
133+
};
134+
}
135+
```
136+
137+
## Multi-Turn Conversations
138+
139+
Use `thread_id` to maintain conversation context across multiple calls:
140+
141+
```typescript
142+
const threadId = crypto.randomUUID();
143+
144+
// First turn
145+
const stream1 = await env.MY_AGENT.STREAM({
146+
messages: [{ role: "user", parts: [{ type: "text", text: "What's the weather?" }] }],
147+
thread_id: threadId,
148+
});
149+
150+
// Second turn — agent remembers the first
151+
const stream2 = await env.MY_AGENT.STREAM({
152+
messages: [{ role: "user", parts: [{ type: "text", text: "How about tomorrow?" }] }],
153+
thread_id: threadId,
154+
memory: { windowSize: 10, thread_id: threadId },
155+
});
156+
```
157+
158+
## Standalone Client
159+
160+
If you need to call a decopilot agent outside the binding system (e.g. from a script or external service), use `createDecopilotClient`:
161+
162+
```typescript
163+
import { createDecopilotClient } from "@decocms/runtime/decopilot";
164+
165+
const client = createDecopilotClient({
166+
baseUrl: "https://mesh.decocms.com/api",
167+
orgSlug: "my-org",
168+
token: "your-api-key",
169+
});
170+
171+
const stream = await client.stream({
172+
agent: { id: "agent-uuid" },
173+
messages: [{ role: "user", parts: [{ type: "text", text: "Hello" }] }],
174+
});
175+
176+
for await (const message of stream) {
177+
console.log(message);
178+
}
179+
```
180+
181+
<Callout type="info">
182+
The standalone client uses the same streaming protocol as the binding proxy. The binding approach is preferred for MCP Apps since it handles authentication and agent resolution automatically.
183+
</Callout>
184+
185+
## How It Works
186+
187+
When the runtime resolves your `StateSchema`, it detects `AgentOf()` fields by the `__type: "@deco/agent"` marker and creates a proxy client. When you call `STREAM`, the proxy:
188+
189+
1. Resolves the agent ID from the binding configuration
190+
2. Merges your parameters with the binding defaults (credential, models, temperature, etc.)
191+
3. Sends a POST request to the decopilot runtime stream endpoint
192+
4. Parses the SSE response into an async iterable of `UIMessage` objects
193+
194+
The runtime stream endpoint (`/:org/decopilot/runtime/stream`) handles model permission checks and falls back to the organization's default AI provider if no explicit models are configured.
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
---
2+
title: Agent Bindings
3+
description: Conecte agentes de IA ao seu MCP App e faça streaming de respostas programaticamente
4+
icon: Bot
5+
---
6+
import Callout from "../../../../components/ui/Callout.astro";
7+
8+
## Visao Geral
9+
10+
Agent bindings permitem que seu MCP App chame [agentes do decopilot](/pt-br/mcp-mesh/agents) diretamente de tool handlers. Isso permite criar tools que delegam trabalho a agentes de IA — por exemplo, uma tool de suporte ao cliente que faz streaming do raciocinio do agente de volta ao usuario.
11+
12+
Agent bindings funcionam como [connection bindings](/pt-br/full-code-guides/building-tools) (`BindingOf`), mas em vez de chamar tools MCP em uma conexao, eles fazem streaming de mensagens de e para um agente do decopilot.
13+
14+
## Declarando um Agent Binding
15+
16+
Use `AgentOf()` no seu `StateSchema` para declarar um agent binding:
17+
18+
```typescript
19+
// api/types/env.ts
20+
import type { DefaultEnv } from "@decocms/runtime";
21+
import { AgentOf } from "@decocms/runtime";
22+
import { z } from "zod";
23+
24+
export const StateSchema = z.object({
25+
MY_AGENT: AgentOf(),
26+
});
27+
28+
export type Env = DefaultEnv<typeof StateSchema>;
29+
```
30+
31+
Quando alguem instala seu MCP App, a interface admin mostra um seletor para o campo `MY_AGENT` onde podem escolher um agente da organizacao.
32+
33+
Em runtime, `MY_AGENT` resolve para um client com um metodo `STREAM` — voce nunca precisa lidar com HTTP ou SSE diretamente.
34+
35+
## Usando o Agente em uma Tool
36+
37+
Uma vez declarado, o agente resolvido esta disponivel em `env.MY_AGENT`:
38+
39+
```typescript
40+
import { createTool } from "@decocms/runtime/tools";
41+
import { z } from "zod";
42+
import type { Env } from "../types/env.ts";
43+
44+
export const askAgentTool = (env: Env) =>
45+
createTool({
46+
id: "ask_agent",
47+
description: "Pergunte ao agente configurado e faca streaming da resposta",
48+
inputSchema: z.object({
49+
question: z.string().describe("A pergunta a fazer"),
50+
}),
51+
outputSchema: z.object({
52+
answer: z.string(),
53+
}),
54+
execute: async ({ context }) => {
55+
const stream = await env.MY_AGENT.STREAM({
56+
messages: [{ role: "user", parts: [{ type: "text", text: context.question }] }],
57+
});
58+
59+
let answer = "";
60+
for await (const message of stream) {
61+
for (const part of message.parts) {
62+
if (part.type === "text") {
63+
answer += part.text;
64+
}
65+
}
66+
}
67+
68+
return { answer };
69+
},
70+
});
71+
```
72+
73+
## Parametros do STREAM
74+
75+
O metodo `STREAM` aceita os seguintes parametros:
76+
77+
```typescript
78+
interface AgentStreamParams {
79+
/** Mensagens para enviar ao agente (obrigatorio) */
80+
messages: Omit<UIMessage, "id">[];
81+
82+
/** Sobrescrever a credencial usada para acesso ao modelo */
83+
credentialId?: string;
84+
85+
/** Sobrescrever configuracoes de modelo */
86+
thinking?: AgentModelInfo;
87+
coding?: AgentModelInfo;
88+
fast?: AgentModelInfo;
89+
90+
/** Nivel de aprovacao de tools: "auto" | "readonly" | "plan" */
91+
toolApprovalLevel?: "auto" | "readonly" | "plan";
92+
93+
/** Temperatura de sampling */
94+
temperature?: number;
95+
96+
/** Configuracao de memoria para continuidade da conversa */
97+
memory?: { windowSize: number; thread_id: string };
98+
99+
/** Thread ID para conversas multi-turn */
100+
thread_id?: string;
101+
}
102+
```
103+
104+
Os parametros de modelo (`thinking`, `coding`, `fast`), `credentialId`, `toolApprovalLevel` e `temperature` tem defaults da configuracao do binding definida na instalacao. Parametros passados ao `STREAM` sobrescrevem esses defaults.
105+
106+
### Niveis de Modelo
107+
108+
Agentes podem usar ate tres niveis de modelo:
109+
110+
| Nivel | Proposito |
111+
|-------|-----------|
112+
| `thinking` | Modelo principal para raciocinio e tarefas complexas |
113+
| `coding` | Otimizado para geracao de codigo (volta ao thinking se nao configurado) |
114+
| `fast` | Modelo leve para respostas rapidas e simples |
115+
116+
## Conversas Multi-Turn
117+
118+
Use `thread_id` para manter contexto da conversa entre multiplas chamadas:
119+
120+
```typescript
121+
const threadId = crypto.randomUUID();
122+
123+
// Primeiro turno
124+
const stream1 = await env.MY_AGENT.STREAM({
125+
messages: [{ role: "user", parts: [{ type: "text", text: "Qual o clima?" }] }],
126+
thread_id: threadId,
127+
});
128+
129+
// Segundo turno — o agente lembra do primeiro
130+
const stream2 = await env.MY_AGENT.STREAM({
131+
messages: [{ role: "user", parts: [{ type: "text", text: "E amanha?" }] }],
132+
thread_id: threadId,
133+
memory: { windowSize: 10, thread_id: threadId },
134+
});
135+
```
136+
137+
## Client Standalone
138+
139+
Se voce precisa chamar um agente do decopilot fora do sistema de bindings (ex: de um script ou servico externo), use `createDecopilotClient`:
140+
141+
```typescript
142+
import { createDecopilotClient } from "@decocms/runtime/decopilot";
143+
144+
const client = createDecopilotClient({
145+
baseUrl: "https://mesh.decocms.com/api",
146+
orgSlug: "minha-org",
147+
token: "sua-api-key",
148+
});
149+
150+
const stream = await client.stream({
151+
agent: { id: "agent-uuid" },
152+
messages: [{ role: "user", parts: [{ type: "text", text: "Ola" }] }],
153+
});
154+
155+
for await (const message of stream) {
156+
console.log(message);
157+
}
158+
```
159+
160+
<Callout type="info">
161+
O client standalone usa o mesmo protocolo de streaming que o proxy de binding. A abordagem via binding e preferida para MCP Apps pois lida com autenticacao e resolucao do agente automaticamente.
162+
</Callout>
163+
164+
## Como Funciona
165+
166+
Quando o runtime resolve seu `StateSchema`, ele detecta campos `AgentOf()` pelo marcador `__type: "@deco/agent"` e cria um client proxy. Quando voce chama `STREAM`, o proxy:
167+
168+
1. Resolve o ID do agente a partir da configuracao do binding
169+
2. Mescla seus parametros com os defaults do binding (credencial, modelos, temperatura, etc.)
170+
3. Envia um POST request para o endpoint de streaming do decopilot runtime
171+
4. Parseia a resposta SSE em um async iterable de objetos `UIMessage`
172+
173+
O endpoint de streaming do runtime (`/:org/decopilot/runtime/stream`) lida com checagem de permissoes de modelo e faz fallback para o provedor de IA padrao da organizacao se nenhum modelo explicito for configurado.

apps/docs/client/src/utils/navigation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export async function getNavigationLinks(
3737
"mcp-mesh/virtual-mcps",
3838
"mcp-mesh/projects",
3939
"mcp-mesh/agents",
40+
"mcp-mesh/agent-bindings",
4041
"mcp-mesh/automations",
4142

4243
// 6. Decopilot

0 commit comments

Comments
 (0)