Skip to content
Merged
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
8 changes: 4 additions & 4 deletions docs/api-reference/veryfront/tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ Create a typed tool definition.
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `createContext7ToolSource` | Create context7 tool source. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/context7.ts#L28) |
| `createProjectScopedRemoteToolCatalog` | Create project scoped remote tool catalog. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/project-scoped-remote-tools.ts#L348) |
| `createRemoteMCPToolSource` | Create a remote MCP source with the framework's guarded outbound transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/remote-mcp.ts#L983) |
| `createRemoteMCPToolSourceFactoryWithTransport` | Create a remote MCP source factory with narrowly scoped host transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/remote-mcp.ts#L1038) |
| `createRemoteMCPToolSource` | Create a remote MCP source with the framework's guarded outbound transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/remote-mcp.ts#L1091) |
| `createRemoteMCPToolSourceFactoryWithTransport` | Create a remote MCP source factory with narrowly scoped host transport. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/remote-mcp.ts#L1146) |
| `createSleepTool` | Create sleep tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/sleep.ts#L54) |
| `createToolsFromHostDefinitions` | Create tools from host definitions. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/host-tools.ts#L96) |
| `createToolsFromRemoteDefinitions` | Create tools from remote definitions. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/remote-source-tools.ts#L29) |
Expand Down Expand Up @@ -169,8 +169,8 @@ Create a typed tool definition.
| `ProjectScopedRemoteToolExecution` | Public API contract for project scoped remote tool execution. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/project-scoped-remote-tools.ts#L45) |
| `ProjectScopedRemoteToolExecutionInput` | Input payload for project scoped remote tool execution. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/project-scoped-remote-tools.ts#L38) |
| `ProjectScopedRemoteToolOptions` | Options accepted by project scoped remote tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/project-scoped-remote-tools.ts#L6) |
| `RemoteMCPToolSourceConfig` | Configuration used by remote MCP tool source. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/remote-mcp.ts#L52) |
| `RemoteMCPToolSourceTransportOptions` | Deployment-owned transport policy for trusted MCP endpoint roots. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/remote-mcp.ts#L990) |
| `RemoteMCPToolSourceConfig` | Configuration used by remote MCP tool source. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/remote-mcp.ts#L53) |
| `RemoteMCPToolSourceTransportOptions` | Deployment-owned transport policy for trusted MCP endpoint roots. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/remote-mcp.ts#L1098) |
| `RemoteToolMaterializationOptions` | Options accepted by remote tool materialization. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/remote-source-tools.ts#L8) |
| `RemoteToolSource` | Remote tool source loaded dynamically at runtime. Hosts can provide these to expose tools from remote MCP-compatible systems without registering those tools globally inside the framework. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/types.ts#L231) |
| `SleepToolInput` | Input payload for sleep tool. | [source](https://github.com/veryfront/veryfront-code/blob/main/src/tool/sleep.ts#L45) |
Expand Down
12 changes: 9 additions & 3 deletions src/agent/conversation/legacy-run-read-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,13 @@ describe("conversation run lifecycle read adapter", () => {
}
});

it("round trips provider-executed error results through durable v2 as AG-UI tool errors", () => {
it("round trips provider-executed structured errors through durable v2", () => {
const diagnostic = {
error: "invalid_skill",
code: "invalid_skill",
message: "Skill validation failed",
request_id: "request-123",
};
const durableEvents = writeDurableEvents(frames([
{
event: {
Expand All @@ -479,7 +485,7 @@ describe("conversation run lifecycle read adapter", () => {
type: "provider_tool_result",
toolCallId: "provider-err",
toolName: "web_search",
output: "provider failed",
output: diagnostic,
isError: true,
providerExecuted: true,
},
Expand All @@ -495,7 +501,7 @@ describe("conversation run lifecycle read adapter", () => {
event: "ToolCallResult",
payload: {
toolCallId: "provider-err",
result: "provider failed",
result: diagnostic,
isError: true,
},
}],
Expand Down
144 changes: 143 additions & 1 deletion src/tool/remote-mcp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
MAX_REMOTE_MCP_TOOL_LIST_PAGES,
MAX_REMOTE_MCP_TOOL_LIST_RESPONSE_BYTES,
} from "./remote-mcp.ts";
import { getToolResultError } from "./result.ts";

describe("tool/remote-mcp", () => {
it("uses host transport only for an exact trusted endpoint", async () => {
Expand Down Expand Up @@ -522,6 +523,65 @@ describe("tool/remote-mcp", () => {
});
});

it("falls back to MCP text diagnostics when structuredContent is empty", async () => {
const source = createRemoteMCPToolSource({
id: "veryfront-mcp",
endpoint: "https://93.184.216.34/mcp",
});

const result = await withMockFetch(async () =>
Response.json({
jsonrpc: "2.0",
id: "veryfront-mcp:tools:call:create_agent",
result: {
isError: true,
structuredContent: {},
content: [{
text: "[already_exists] Agent already exists",
}],
},
}), async () =>
await source.executeTool("create_agent", { id: "writer" }, {
toolCallId: "tool-call-123",
}));

assertEquals(result, {
error: "already_exists",
code: "already_exists",
message: "[already_exists] Agent already exists",
correlation_id: "tool-call-123",
});
assertEquals(getToolResultError(result), "[already_exists] Agent already exists");
});

it("bounds local tool-call correlation ids in text error results", async () => {
const source = createRemoteMCPToolSource({
id: "veryfront-mcp",
endpoint: "https://93.184.216.34/mcp",
});
const oversizedToolCallId = `tool-${"x".repeat(252)}`;

const result = await withMockFetch(async () =>
Response.json({
jsonrpc: "2.0",
id: "veryfront-mcp:tools:call:create_agent",
result: {
isError: true,
content: [{ text: "Agent already exists" }],
},
}), async () =>
await source.executeTool("create_agent", { id: "writer" }, {
toolCallId: oversizedToolCallId,
}));

assertEquals(result, {
error: "tool_error",
code: "tool_error",
message: "Agent already exists",
correlation_id: oversizedToolCallId.slice(0, 256),
});
});

it("wraps non-object structured MCP errors with a canonical marker", async () => {
const source = createRemoteMCPToolSource({
id: "docs",
Expand Down Expand Up @@ -565,7 +625,8 @@ describe("tool/remote-mcp", () => {
}), async () => await source.executeTool("search_docs", { query: "auth" }));

assertEquals(result, {
error: "tool_error",
error: "rate_limited",
code: "rate_limited",
message: "Try again later",
});
});
Expand Down Expand Up @@ -633,6 +694,68 @@ describe("tool/remote-mcp", () => {
});
});

it("returns structured JSON-RPC tool errors with correlation context", async () => {
const source = createRemoteMCPToolSource({
id: "veryfront-mcp",
endpoint: "https://93.184.216.34/mcp",
});

const result = await withMockFetch(async () =>
Response.json({
jsonrpc: "2.0",
id: "veryfront-mcp:tools:call:update_skill",
error: {
code: -32602,
message: "Skill validation failed",
data: {
code: "invalid_skill",
request_id: "request-123",
field: "instructions",
},
},
}), async () => await source.executeTool("update_skill", { skill_id: "writer" }));

assertEquals(result, {
error: "invalid_skill",
code: "invalid_skill",
message: "Skill validation failed",
request_id: "request-123",
json_rpc_code: -32602,
});
assertEquals(getToolResultError(result), "Skill validation failed");
});

it("rejects malformed JSON-RPC tool error envelopes", async () => {
const source = createRemoteMCPToolSource({
id: "veryfront-mcp",
endpoint: "https://93.184.216.34/mcp",
});

for (
const error of [
null,
"failed",
{ message: "Missing error code" },
{ code: -32603, message: " " },
]
) {
await assertRejects(
() =>
withMockFetch(
async () =>
Response.json({
jsonrpc: "2.0",
id: "veryfront-mcp:tools:call:update_skill",
error,
}),
async () => await source.executeTool("update_skill", {}),
),
Error,
"malformed JSON-RPC error object",
);
}
});

it("normalizes HTTP invalid_grant failures into reconnect-required tool output", async () => {
const source = createRemoteMCPToolSource({
id: "veryfront-mcp",
Expand Down Expand Up @@ -676,6 +799,25 @@ describe("tool/remote-mcp", () => {
assertEquals(error.message, "Remote MCP request failed (500)");
});

it("keeps unexpected tool transport failures on the exception path", async () => {
const source = createRemoteMCPToolSource({
id: "veryfront-mcp",
endpoint: "https://93.184.216.34/mcp",
});

const error = await assertRejects(
() =>
withMockFetch(
async () => new Response("private payload <TOKEN>", { status: 503 }),
async () => await source.executeTool("create_agent", { id: "writer" }),
),
Error,
);

assertInstanceOf(error, Error);
assertEquals(error.message, "Remote MCP request failed (503)");
});

it("preserves caller accept types while adding the MCP-required media types", async () => {
let acceptHeader = "";

Expand Down
Loading