Tool reply meta (#6074)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2025-12-11 19:43:36 -05:00
committed by GitHub
parent 7dd244eff6
commit cdab2fc3a6
52 changed files with 1852 additions and 1083 deletions
@@ -68,7 +68,7 @@ impl Evaluation for DeveloperImage {
if let MessageContent::ToolResponse(tool_resp) = content {
if let Ok(result) = &tool_resp.tool_result {
// Check each item in the result list
for item in result {
for item in &result.content {
if let Some(image) = item.as_image() {
// Image content already contains mime_type and data
if image.mime_type.starts_with("image/")
+6 -5
View File
@@ -11,7 +11,7 @@ use goose::mcp_utils::ToolResult;
use goose::providers::create;
use goose::session::session_manager::SessionType;
use goose::session::SessionManager;
use rmcp::model::{Content, RawContent, ResourceContents, Role};
use rmcp::model::{CallToolResult, RawContent, ResourceContents, Role};
use std::collections::{HashMap, HashSet};
use std::fs;
use std::sync::Arc;
@@ -74,8 +74,8 @@ fn extract_tool_locations(
.and_then(|c| c.as_str());
// Extract line numbers from the response content
if let Ok(content_items) = &tool_response.tool_result {
for content in content_items {
if let Ok(result) = &tool_response.tool_result {
for content in &result.content {
if let RawContent::Text(text_content) = &content.raw {
let text = &text_content.text;
@@ -491,9 +491,10 @@ impl GooseAcpAgent {
}
/// Build tool call content from tool result
fn build_tool_call_content(tool_result: &ToolResult<Vec<Content>>) -> Vec<ToolCallContent> {
fn build_tool_call_content(tool_result: &ToolResult<CallToolResult>) -> Vec<ToolCallContent> {
match tool_result {
Ok(content_items) => content_items
Ok(result) => result
.content
.iter()
.filter_map(|content| match &content.raw {
RawContent::Text(val) => Some(ToolCallContent::Content {
@@ -1,12 +1,12 @@
{
"74a3cc715c4c65dcae4ecec084d962947e4ce3cf02a8b639ee23b080e88bf6ec": {
"1b90ffd4b16eab7aef1c24e9b61f6b6cc6f0d9b04e8a4f4344454a45b0793284": {
"input": {
"system": "You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.\ngoose is being developed as an open-source software project.\n\ngoose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,\nclaude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).\nThese models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10\nmonths prior to the current date.\n\n# Extensions\n\nExtensions allow other applications to provide context to goose. Extensions connect goose to different data sources and\ntools.\nYou are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level\nproblems using the tools in these extensions, and can interact with multiple at once.\n\nIf the Extension Manager extension is enabled, you can use the search_available_extensions tool to discover additional\nextensions that can help with your task. To enable or disable extensions, use the manage_extensions tool with the\nextension_name. You should only enable extensions found from the search_available_extensions tool.\nIf Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load\nnew ones.\n\nBecause you dynamically load extensions, your conversation history may refer\nto interactions with extensions that are not currently active. The currently\nactive extensions are below. Each of these extensions provides tools that are\nin your tool specification.\n\n\n## weather_extension\n\n\n\n\n\n\n# Response Guidelines\n\n- Use Markdown formatting for all responses.\n- Follow best practices for Markdown, including:\n - Using headers for organization.\n - Bullet points for lists.\n - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic\n links using angle brackets (e.g., <http://example.com/>).\n- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the\n language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting.\n- Ensure clarity, conciseness, and proper formatting to enhance readability and usability.",
"messages": [
{
"id": "msg_20251124_8_0",
"id": "msg_20251211_27_0",
"role": "user",
"created": 1763989805,
"created": 1765495263,
"content": [
{
"type": "text",
@@ -19,13 +19,13 @@
}
},
{
"id": "msg_b4135f6c-9a29-4904-a59f-632ea849ef5a",
"id": "msg_9076da8a-a3b1-4cfc-b5d9-0ffc2a7879c9",
"role": "assistant",
"created": 1763989808,
"created": 1765495266,
"content": [
{
"type": "toolRequest",
"id": "toolu_01VxSzmLcPK7UNNE4ms8XRa6",
"id": "toolu_01CDkt9pjh8oC9C1984uhvuG",
"toolCall": {
"status": "success",
"value": {
@@ -43,21 +43,23 @@
}
},
{
"id": "msg_253b4067-0c08-4807-8b36-23b576f138b5",
"id": "msg_d7b3db5e-0402-42a8-937c-2ccf68a3e4e0",
"role": "user",
"created": 1763989808,
"created": 1765495266,
"content": [
{
"type": "toolResponse",
"id": "toolu_01VxSzmLcPK7UNNE4ms8XRa6",
"id": "toolu_01CDkt9pjh8oC9C1984uhvuG",
"toolResult": {
"status": "success",
"value": [
{
"type": "text",
"text": "The weather in Berlin, Germany is cloudy and 18°C"
}
]
"value": {
"content": [
{
"type": "text",
"text": "The weather in Berlin, Germany is cloudy and 18°C"
}
]
}
}
}
],
@@ -305,11 +307,11 @@
"message": {
"id": null,
"role": "assistant",
"created": 1763989811,
"created": 1765495269,
"content": [
{
"type": "text",
"text": "The weather in Berlin, Germany is currently **cloudy** with a temperature of **18°C** (64°F)."
"text": "# Weather in Berlin, Germany\n\nThe current weather in Berlin, Germany is:\n- **Conditions**: Cloudy\n- **Temperature**: 18°C (64°F)\n\nIt's a mild day with overcast conditions in Berlin!"
}
],
"metadata": {
@@ -321,301 +323,8 @@
"model": "claude-sonnet-4-20250514",
"usage": {
"input_tokens": 2406,
"output_tokens": 29,
"total_tokens": 2435
}
}
}
},
"1bc400a528c54b25f4f1f609481e98e44222b3deaf7eee2c9e640e6345c73861": {
"input": {
"system": "You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.\ngoose is being developed as an open-source software project.\n\ngoose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,\nclaude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).\nThese models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10\nmonths prior to the current date.\n\n# Extensions\n\nExtensions allow other applications to provide context to goose. Extensions connect goose to different data sources and\ntools.\nYou are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level\nproblems using the tools in these extensions, and can interact with multiple at once.\n\nIf the Extension Manager extension is enabled, you can use the search_available_extensions tool to discover additional\nextensions that can help with your task. To enable or disable extensions, use the manage_extensions tool with the\nextension_name. You should only enable extensions found from the search_available_extensions tool.\nIf Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load\nnew ones.\n\nBecause you dynamically load extensions, your conversation history may refer\nto interactions with extensions that are not currently active. The currently\nactive extensions are below. Each of these extensions provides tools that are\nin your tool specification.\n\n\n## weather_extension\n\n\n\n\n\n\n# Response Guidelines\n\n- Use Markdown formatting for all responses.\n- Follow best practices for Markdown, including:\n - Using headers for organization.\n - Bullet points for lists.\n - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic\n links using angle brackets (e.g., <http://example.com/>).\n- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the\n language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting.\n- Ensure clarity, conciseness, and proper formatting to enhance readability and usability.",
"messages": [
{
"id": "msg_20251124_8_0",
"role": "user",
"created": 1763989805,
"content": [
{
"type": "text",
"text": "tell me what the weather is in Berlin, Germany"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
}
],
"tools": [
{
"name": "dynamic_task__create_task",
"description": "Create tasks with instructions or prompt. For simple tasks, only include the instructions field. Extensions control: omit field = use all current extensions; empty array [] = no extensions; array with names = only those extensions. Specify extensions as shortnames (the prefixes for your tools). Specify return_last_only as true and have your subagent summarize its work in its last message to conserve your own context. Optional: title, description, extensions, settings, retry, response schema, activities. Arrays for multiple tasks.",
"inputSchema": {
"$defs": {
"TaskParameter": {
"description": "Parameters for a single task",
"properties": {
"activities": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"description": {
"type": [
"string",
"null"
]
},
"extensions": {
"items": {
"additionalProperties": true,
"type": "object"
},
"type": [
"array",
"null"
]
},
"instructions": {
"type": [
"string",
"null"
]
},
"parameters": {
"items": {
"additionalProperties": true,
"type": "object"
},
"type": [
"array",
"null"
]
},
"prompt": {
"type": [
"string",
"null"
]
},
"response": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"retry": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"return_last_only": {
"description": "If true, return only the last message from the subagent (default: false, returns full conversation)",
"type": [
"boolean",
"null"
]
},
"settings": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"title": {
"type": [
"string",
"null"
]
}
},
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"execution_mode": {
"description": "How to execute multiple tasks (default: parallel for multiple tasks, sequential for single task)",
"type": [
"string",
"null"
]
},
"task_parameters": {
"description": "Array of tasks. Each task must have either 'instructions' OR 'prompt' field (at least one is required).",
"items": {
"$ref": "#/$defs/TaskParameter"
},
"minItems": 1,
"type": "array"
}
},
"required": [
"task_parameters"
],
"title": "CreateDynamicTaskParams",
"type": "object"
},
"annotations": {
"title": "Create Dynamic Tasks",
"readOnlyHint": false,
"destructiveHint": false,
"idempotentHint": false,
"openWorldHint": true
}
},
{
"name": "platform__manage_schedule",
"description": "Manage scheduled recipe execution for this goose instance.\n\nActions:\n- \"list\": List all scheduled jobs\n- \"create\": Create a new scheduled job from a recipe file\n- \"run_now\": Execute a scheduled job immediately \n- \"pause\": Pause a scheduled job\n- \"unpause\": Resume a paused job\n- \"delete\": Remove a scheduled job\n- \"kill\": Terminate a currently running job\n- \"inspect\": Get details about a running job\n- \"sessions\": List execution history for a job\n- \"session_content\": Get the full content (messages) of a specific session\n",
"inputSchema": {
"properties": {
"action": {
"enum": [
"list",
"create",
"run_now",
"pause",
"unpause",
"delete",
"kill",
"inspect",
"sessions",
"session_content"
],
"type": "string"
},
"cron_expression": {
"description": "A cron expression for create action. Supports both 5-field (minute hour day month weekday) and 6-field (second minute hour day month weekday) formats. 5-field expressions are automatically converted to 6-field by prepending '0' for seconds.",
"type": "string"
},
"job_id": {
"description": "Job identifier for operations on existing jobs",
"type": "string"
},
"limit": {
"default": 50,
"description": "Limit for sessions list",
"type": "integer"
},
"recipe_path": {
"description": "Path to recipe file for create action",
"type": "string"
},
"session_id": {
"description": "Session identifier for session_content action",
"type": "string"
}
},
"required": [
"action"
],
"type": "object"
},
"annotations": {
"title": "Manage scheduled recipes",
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": false
}
},
{
"name": "subagent__execute_task",
"description": "Only use the subagent__execute_task tool when you execute sub recipe task or dynamic task.\n EXECUTION STRATEGY DECISION:\n 1. If the tasks are created with execution_mode, use the execution_mode.\n 2. Execute tasks sequentially unless user explicitly requests parallel execution. PARALLEL: User uses keywords like 'parallel', 'simultaneously', 'at the same time', 'concurrently'\n\n IMPLEMENTATION:\n - Sequential execution: Call this tool multiple times, passing exactly ONE task per call\n - Parallel execution: Call this tool once, passing an ARRAY of all tasks\n\n EXAMPLES:\n User Intent Based:\n - User: 'get weather and tell me a joke' → Sequential (2 separate tool calls, 1 task each)\n - User: 'get weather and joke in parallel' → Parallel (1 tool call with array of 2 tasks)\n - User: 'run these simultaneously' → Parallel (1 tool call with task array)\n - User: 'do task A then task B' → Sequential (2 separate tool calls)",
"inputSchema": {
"properties": {
"execution_mode": {
"default": "sequential",
"description": "Execution strategy for multiple tasks. Use 'sequential' (default) unless user explicitly requests parallel execution with words like 'parallel', 'simultaneously', 'at the same time', or 'concurrently'.",
"enum": [
"sequential",
"parallel"
],
"type": "string"
},
"task_ids": {
"items": {
"description": "Unique identifier for the task",
"type": "string"
},
"type": "array"
}
},
"required": [
"task_ids"
],
"type": "object"
},
"annotations": {
"title": "Run tasks in parallel",
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": true
}
},
{
"name": "weather_extension__get_weather",
"description": "Get the weather for a location",
"inputSchema": {
"properties": {
"location": {
"description": "The city and state, e.g. San Francisco, CA",
"type": "string"
}
},
"required": [
"location"
],
"type": "object"
}
}
]
},
"output": {
"message": {
"id": null,
"role": "assistant",
"created": 1763989808,
"content": [
{
"type": "text",
"text": "I'll get the current weather information for Berlin, Germany."
},
{
"type": "toolRequest",
"id": "toolu_01VxSzmLcPK7UNNE4ms8XRa6",
"toolCall": {
"status": "success",
"value": {
"name": "weather_extension__get_weather",
"arguments": {
"location": "Berlin, Germany"
}
}
}
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
},
"usage": {
"model": "claude-sonnet-4-20250514",
"usage": {
"input_tokens": 2320,
"output_tokens": 72,
"total_tokens": 2392
"output_tokens": 55,
"total_tokens": 2461
}
}
}
@@ -627,7 +336,7 @@
{
"id": null,
"role": "user",
"created": 1763989805,
"created": 1765495263,
"content": [
{
"type": "text",
@@ -646,7 +355,7 @@
"message": {
"id": null,
"role": "assistant",
"created": 1763989810,
"created": 1765495265,
"content": [
{
"type": "text",
@@ -667,5 +376,298 @@
}
}
}
},
"1bc400a528c54b25f4f1f609481e98e44222b3deaf7eee2c9e640e6345c73861": {
"input": {
"system": "You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.\ngoose is being developed as an open-source software project.\n\ngoose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,\nclaude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).\nThese models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10\nmonths prior to the current date.\n\n# Extensions\n\nExtensions allow other applications to provide context to goose. Extensions connect goose to different data sources and\ntools.\nYou are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level\nproblems using the tools in these extensions, and can interact with multiple at once.\n\nIf the Extension Manager extension is enabled, you can use the search_available_extensions tool to discover additional\nextensions that can help with your task. To enable or disable extensions, use the manage_extensions tool with the\nextension_name. You should only enable extensions found from the search_available_extensions tool.\nIf Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load\nnew ones.\n\nBecause you dynamically load extensions, your conversation history may refer\nto interactions with extensions that are not currently active. The currently\nactive extensions are below. Each of these extensions provides tools that are\nin your tool specification.\n\n\n## weather_extension\n\n\n\n\n\n\n# Response Guidelines\n\n- Use Markdown formatting for all responses.\n- Follow best practices for Markdown, including:\n - Using headers for organization.\n - Bullet points for lists.\n - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic\n links using angle brackets (e.g., <http://example.com/>).\n- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the\n language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting.\n- Ensure clarity, conciseness, and proper formatting to enhance readability and usability.",
"messages": [
{
"id": "msg_20251211_27_0",
"role": "user",
"created": 1765495263,
"content": [
{
"type": "text",
"text": "tell me what the weather is in Berlin, Germany"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
}
],
"tools": [
{
"name": "dynamic_task__create_task",
"description": "Create tasks with instructions or prompt. For simple tasks, only include the instructions field. Extensions control: omit field = use all current extensions; empty array [] = no extensions; array with names = only those extensions. Specify extensions as shortnames (the prefixes for your tools). Specify return_last_only as true and have your subagent summarize its work in its last message to conserve your own context. Optional: title, description, extensions, settings, retry, response schema, activities. Arrays for multiple tasks.",
"inputSchema": {
"$defs": {
"TaskParameter": {
"description": "Parameters for a single task",
"properties": {
"activities": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"description": {
"type": [
"string",
"null"
]
},
"extensions": {
"items": {
"additionalProperties": true,
"type": "object"
},
"type": [
"array",
"null"
]
},
"instructions": {
"type": [
"string",
"null"
]
},
"parameters": {
"items": {
"additionalProperties": true,
"type": "object"
},
"type": [
"array",
"null"
]
},
"prompt": {
"type": [
"string",
"null"
]
},
"response": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"retry": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"return_last_only": {
"description": "If true, return only the last message from the subagent (default: false, returns full conversation)",
"type": [
"boolean",
"null"
]
},
"settings": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"title": {
"type": [
"string",
"null"
]
}
},
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"execution_mode": {
"description": "How to execute multiple tasks (default: parallel for multiple tasks, sequential for single task)",
"type": [
"string",
"null"
]
},
"task_parameters": {
"description": "Array of tasks. Each task must have either 'instructions' OR 'prompt' field (at least one is required).",
"items": {
"$ref": "#/$defs/TaskParameter"
},
"minItems": 1,
"type": "array"
}
},
"required": [
"task_parameters"
],
"title": "CreateDynamicTaskParams",
"type": "object"
},
"annotations": {
"title": "Create Dynamic Tasks",
"readOnlyHint": false,
"destructiveHint": false,
"idempotentHint": false,
"openWorldHint": true
}
},
{
"name": "platform__manage_schedule",
"description": "Manage scheduled recipe execution for this goose instance.\n\nActions:\n- \"list\": List all scheduled jobs\n- \"create\": Create a new scheduled job from a recipe file\n- \"run_now\": Execute a scheduled job immediately \n- \"pause\": Pause a scheduled job\n- \"unpause\": Resume a paused job\n- \"delete\": Remove a scheduled job\n- \"kill\": Terminate a currently running job\n- \"inspect\": Get details about a running job\n- \"sessions\": List execution history for a job\n- \"session_content\": Get the full content (messages) of a specific session\n",
"inputSchema": {
"properties": {
"action": {
"enum": [
"list",
"create",
"run_now",
"pause",
"unpause",
"delete",
"kill",
"inspect",
"sessions",
"session_content"
],
"type": "string"
},
"cron_expression": {
"description": "A cron expression for create action. Supports both 5-field (minute hour day month weekday) and 6-field (second minute hour day month weekday) formats. 5-field expressions are automatically converted to 6-field by prepending '0' for seconds.",
"type": "string"
},
"job_id": {
"description": "Job identifier for operations on existing jobs",
"type": "string"
},
"limit": {
"default": 50,
"description": "Limit for sessions list",
"type": "integer"
},
"recipe_path": {
"description": "Path to recipe file for create action",
"type": "string"
},
"session_id": {
"description": "Session identifier for session_content action",
"type": "string"
}
},
"required": [
"action"
],
"type": "object"
},
"annotations": {
"title": "Manage scheduled recipes",
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": false
}
},
{
"name": "subagent__execute_task",
"description": "Only use the subagent__execute_task tool when you execute sub recipe task or dynamic task.\n EXECUTION STRATEGY DECISION:\n 1. If the tasks are created with execution_mode, use the execution_mode.\n 2. Execute tasks sequentially unless user explicitly requests parallel execution. PARALLEL: User uses keywords like 'parallel', 'simultaneously', 'at the same time', 'concurrently'\n\n IMPLEMENTATION:\n - Sequential execution: Call this tool multiple times, passing exactly ONE task per call\n - Parallel execution: Call this tool once, passing an ARRAY of all tasks\n\n EXAMPLES:\n User Intent Based:\n - User: 'get weather and tell me a joke' → Sequential (2 separate tool calls, 1 task each)\n - User: 'get weather and joke in parallel' → Parallel (1 tool call with array of 2 tasks)\n - User: 'run these simultaneously' → Parallel (1 tool call with task array)\n - User: 'do task A then task B' → Sequential (2 separate tool calls)",
"inputSchema": {
"properties": {
"execution_mode": {
"default": "sequential",
"description": "Execution strategy for multiple tasks. Use 'sequential' (default) unless user explicitly requests parallel execution with words like 'parallel', 'simultaneously', 'at the same time', or 'concurrently'.",
"enum": [
"sequential",
"parallel"
],
"type": "string"
},
"task_ids": {
"items": {
"description": "Unique identifier for the task",
"type": "string"
},
"type": "array"
}
},
"required": [
"task_ids"
],
"type": "object"
},
"annotations": {
"title": "Run tasks in parallel",
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": true
}
},
{
"name": "weather_extension__get_weather",
"description": "Get the weather for a location",
"inputSchema": {
"properties": {
"location": {
"description": "The city and state, e.g. San Francisco, CA",
"type": "string"
}
},
"required": [
"location"
],
"type": "object"
}
}
]
},
"output": {
"message": {
"id": null,
"role": "assistant",
"created": 1765495266,
"content": [
{
"type": "text",
"text": "I'll get the current weather information for Berlin, Germany for you."
},
{
"type": "toolRequest",
"id": "toolu_01CDkt9pjh8oC9C1984uhvuG",
"toolCall": {
"status": "success",
"value": {
"name": "weather_extension__get_weather",
"arguments": {
"location": "Berlin, Germany"
}
}
}
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
},
"usage": {
"model": "claude-sonnet-4-20250514",
"usage": {
"input_tokens": 2320,
"output_tokens": 74,
"total_tokens": 2394
}
}
}
}
}
@@ -1,12 +1,60 @@
{
"8b1b8633232ca390cb3ff37a48daf74168b58f13e2943efcb17957129ee34d1a": {
"input": {
"system": "Reply with only a description in four words or less",
"messages": [
{
"id": null,
"role": "user",
"created": 1765495269,
"content": [
{
"type": "text",
"text": "Here are the first few user messages:\ntell me what the weather is in Berlin, Germany\n\nBased on the conversation so far, provide a concise description of this session in 4 words or less. This will be used for finding the session later in a UI with limited space - reply *ONLY* with the description"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
}
],
"tools": []
},
"output": {
"message": {
"id": null,
"role": "assistant",
"created": 1765495270,
"content": [
{
"type": "text",
"text": "Weather inquiry for Berlin"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
},
"usage": {
"model": "gpt-4o-mini-2024-07-18",
"usage": {
"input_tokens": 84,
"output_tokens": 5,
"total_tokens": 89
}
}
}
},
"1bc400a528c54b25f4f1f609481e98e44222b3deaf7eee2c9e640e6345c73861": {
"input": {
"system": "You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.\ngoose is being developed as an open-source software project.\n\ngoose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,\nclaude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).\nThese models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10\nmonths prior to the current date.\n\n# Extensions\n\nExtensions allow other applications to provide context to goose. Extensions connect goose to different data sources and\ntools.\nYou are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level\nproblems using the tools in these extensions, and can interact with multiple at once.\n\nIf the Extension Manager extension is enabled, you can use the search_available_extensions tool to discover additional\nextensions that can help with your task. To enable or disable extensions, use the manage_extensions tool with the\nextension_name. You should only enable extensions found from the search_available_extensions tool.\nIf Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load\nnew ones.\n\nBecause you dynamically load extensions, your conversation history may refer\nto interactions with extensions that are not currently active. The currently\nactive extensions are below. Each of these extensions provides tools that are\nin your tool specification.\n\n\n## weather_extension\n\n\n\n\n\n\n# Response Guidelines\n\n- Use Markdown formatting for all responses.\n- Follow best practices for Markdown, including:\n - Using headers for organization.\n - Bullet points for lists.\n - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic\n links using angle brackets (e.g., <http://example.com/>).\n- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the\n language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting.\n- Ensure clarity, conciseness, and proper formatting to enhance readability and usability.",
"messages": [
{
"id": "msg_20251124_9_0",
"id": "msg_20251211_28_0",
"role": "user",
"created": 1763989812,
"created": 1765495269,
"content": [
{
"type": "text",
@@ -257,11 +305,11 @@
"message": {
"id": null,
"role": "assistant",
"created": 1763989813,
"created": 1765495270,
"content": [
{
"type": "toolRequest",
"id": "call_5IgyNY33nTougqsNFhizypew",
"id": "call_JJok3ApyTL54ohjLX8tTYVHC",
"toolCall": {
"status": "success",
"value": {
@@ -288,14 +336,14 @@
}
}
},
"2c07b9a6570269fef4ae329889ce5a5243d7b09b52165580be701b67b8b95818": {
"e020c157caa13bad743162d7079a04ddafca2d075469124a0162c5562a2cf21b": {
"input": {
"system": "You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.\ngoose is being developed as an open-source software project.\n\ngoose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,\nclaude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).\nThese models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10\nmonths prior to the current date.\n\n# Extensions\n\nExtensions allow other applications to provide context to goose. Extensions connect goose to different data sources and\ntools.\nYou are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level\nproblems using the tools in these extensions, and can interact with multiple at once.\n\nIf the Extension Manager extension is enabled, you can use the search_available_extensions tool to discover additional\nextensions that can help with your task. To enable or disable extensions, use the manage_extensions tool with the\nextension_name. You should only enable extensions found from the search_available_extensions tool.\nIf Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load\nnew ones.\n\nBecause you dynamically load extensions, your conversation history may refer\nto interactions with extensions that are not currently active. The currently\nactive extensions are below. Each of these extensions provides tools that are\nin your tool specification.\n\n\n## weather_extension\n\n\n\n\n\n\n# Response Guidelines\n\n- Use Markdown formatting for all responses.\n- Follow best practices for Markdown, including:\n - Using headers for organization.\n - Bullet points for lists.\n - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic\n links using angle brackets (e.g., <http://example.com/>).\n- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the\n language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting.\n- Ensure clarity, conciseness, and proper formatting to enhance readability and usability.",
"messages": [
{
"id": "msg_20251124_9_0",
"id": "msg_20251211_28_0",
"role": "user",
"created": 1763989812,
"created": 1765495269,
"content": [
{
"type": "text",
@@ -308,13 +356,13 @@
}
},
{
"id": "msg_b87436fe-d710-4fcd-b912-a69ab4ccfe7e",
"id": "msg_1d08355f-6d2d-413e-a3b5-787d635f9c6e",
"role": "assistant",
"created": 1763989813,
"created": 1765495270,
"content": [
{
"type": "toolRequest",
"id": "call_5IgyNY33nTougqsNFhizypew",
"id": "call_JJok3ApyTL54ohjLX8tTYVHC",
"toolCall": {
"status": "success",
"value": {
@@ -332,21 +380,23 @@
}
},
{
"id": "msg_9cf859cf-4adc-4464-aba4-1d4ccf9d7d05",
"id": "msg_929b0769-72dd-46f8-9027-31d1c09d4f08",
"role": "user",
"created": 1763989813,
"created": 1765495270,
"content": [
{
"type": "toolResponse",
"id": "call_5IgyNY33nTougqsNFhizypew",
"id": "call_JJok3ApyTL54ohjLX8tTYVHC",
"toolResult": {
"status": "success",
"value": [
{
"type": "text",
"text": "The weather in Berlin, Germany is cloudy and 18°C"
}
]
"value": {
"content": [
{
"type": "text",
"text": "The weather in Berlin, Germany is cloudy and 18°C"
}
]
}
}
}
],
@@ -594,11 +644,11 @@
"message": {
"id": null,
"role": "assistant",
"created": 1763989814,
"created": 1765495271,
"content": [
{
"type": "text",
"text": "### Current Weather in Berlin, Germany\n\n- **Condition:** Cloudy\n- **Temperature:** 18°C"
"text": "### Current Weather in Berlin, Germany\n\n- **Condition:** Cloudy\n- **Temperature:** 18°C\n\nIf you need more information or a forecast, let me know!"
}
],
"metadata": {
@@ -610,56 +660,8 @@
"model": "gpt-4o-mini-2024-07-18",
"usage": {
"input_tokens": 1534,
"output_tokens": 24,
"total_tokens": 1558
}
}
}
},
"8b1b8633232ca390cb3ff37a48daf74168b58f13e2943efcb17957129ee34d1a": {
"input": {
"system": "Reply with only a description in four words or less",
"messages": [
{
"id": null,
"role": "user",
"created": 1763989812,
"content": [
{
"type": "text",
"text": "Here are the first few user messages:\ntell me what the weather is in Berlin, Germany\n\nBased on the conversation so far, provide a concise description of this session in 4 words or less. This will be used for finding the session later in a UI with limited space - reply *ONLY* with the description"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
}
],
"tools": []
},
"output": {
"message": {
"id": null,
"role": "assistant",
"created": 1763989813,
"content": [
{
"type": "text",
"text": "Berlin weather inquiry"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
},
"usage": {
"model": "gpt-4o-mini-2024-07-18",
"usage": {
"input_tokens": 84,
"output_tokens": 4,
"total_tokens": 88
"output_tokens": 38,
"total_tokens": 1572
}
}
}
@@ -1,301 +1,12 @@
{
"1bc400a528c54b25f4f1f609481e98e44222b3deaf7eee2c9e640e6345c73861": {
"075d1e65f3a25bbdfc21aee19a1119b43fd4241f8643e646c9eb928a431975d4": {
"input": {
"system": "You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.\ngoose is being developed as an open-source software project.\n\ngoose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,\nclaude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).\nThese models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10\nmonths prior to the current date.\n\n# Extensions\n\nExtensions allow other applications to provide context to goose. Extensions connect goose to different data sources and\ntools.\nYou are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level\nproblems using the tools in these extensions, and can interact with multiple at once.\n\nIf the Extension Manager extension is enabled, you can use the search_available_extensions tool to discover additional\nextensions that can help with your task. To enable or disable extensions, use the manage_extensions tool with the\nextension_name. You should only enable extensions found from the search_available_extensions tool.\nIf Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load\nnew ones.\n\nBecause you dynamically load extensions, your conversation history may refer\nto interactions with extensions that are not currently active. The currently\nactive extensions are below. Each of these extensions provides tools that are\nin your tool specification.\n\n\n## weather_extension\n\n\n\n\n\n\n# Response Guidelines\n\n- Use Markdown formatting for all responses.\n- Follow best practices for Markdown, including:\n - Using headers for organization.\n - Bullet points for lists.\n - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic\n links using angle brackets (e.g., <http://example.com/>).\n- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the\n language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting.\n- Ensure clarity, conciseness, and proper formatting to enhance readability and usability.",
"messages": [
{
"id": "msg_20251124_10_0",
"id": "msg_20251211_40_0",
"role": "user",
"created": 1763989814,
"content": [
{
"type": "text",
"text": "tell me what the weather is in Berlin, Germany"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
}
],
"tools": [
{
"name": "dynamic_task__create_task",
"description": "Create tasks with instructions or prompt. For simple tasks, only include the instructions field. Extensions control: omit field = use all current extensions; empty array [] = no extensions; array with names = only those extensions. Specify extensions as shortnames (the prefixes for your tools). Specify return_last_only as true and have your subagent summarize its work in its last message to conserve your own context. Optional: title, description, extensions, settings, retry, response schema, activities. Arrays for multiple tasks.",
"inputSchema": {
"$defs": {
"TaskParameter": {
"description": "Parameters for a single task",
"properties": {
"activities": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"description": {
"type": [
"string",
"null"
]
},
"extensions": {
"items": {
"additionalProperties": true,
"type": "object"
},
"type": [
"array",
"null"
]
},
"instructions": {
"type": [
"string",
"null"
]
},
"parameters": {
"items": {
"additionalProperties": true,
"type": "object"
},
"type": [
"array",
"null"
]
},
"prompt": {
"type": [
"string",
"null"
]
},
"response": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"retry": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"return_last_only": {
"description": "If true, return only the last message from the subagent (default: false, returns full conversation)",
"type": [
"boolean",
"null"
]
},
"settings": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"title": {
"type": [
"string",
"null"
]
}
},
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"execution_mode": {
"description": "How to execute multiple tasks (default: parallel for multiple tasks, sequential for single task)",
"type": [
"string",
"null"
]
},
"task_parameters": {
"description": "Array of tasks. Each task must have either 'instructions' OR 'prompt' field (at least one is required).",
"items": {
"$ref": "#/$defs/TaskParameter"
},
"minItems": 1,
"type": "array"
}
},
"required": [
"task_parameters"
],
"title": "CreateDynamicTaskParams",
"type": "object"
},
"annotations": {
"title": "Create Dynamic Tasks",
"readOnlyHint": false,
"destructiveHint": false,
"idempotentHint": false,
"openWorldHint": true
}
},
{
"name": "platform__manage_schedule",
"description": "Manage scheduled recipe execution for this goose instance.\n\nActions:\n- \"list\": List all scheduled jobs\n- \"create\": Create a new scheduled job from a recipe file\n- \"run_now\": Execute a scheduled job immediately \n- \"pause\": Pause a scheduled job\n- \"unpause\": Resume a paused job\n- \"delete\": Remove a scheduled job\n- \"kill\": Terminate a currently running job\n- \"inspect\": Get details about a running job\n- \"sessions\": List execution history for a job\n- \"session_content\": Get the full content (messages) of a specific session\n",
"inputSchema": {
"properties": {
"action": {
"enum": [
"list",
"create",
"run_now",
"pause",
"unpause",
"delete",
"kill",
"inspect",
"sessions",
"session_content"
],
"type": "string"
},
"cron_expression": {
"description": "A cron expression for create action. Supports both 5-field (minute hour day month weekday) and 6-field (second minute hour day month weekday) formats. 5-field expressions are automatically converted to 6-field by prepending '0' for seconds.",
"type": "string"
},
"job_id": {
"description": "Job identifier for operations on existing jobs",
"type": "string"
},
"limit": {
"default": 50,
"description": "Limit for sessions list",
"type": "integer"
},
"recipe_path": {
"description": "Path to recipe file for create action",
"type": "string"
},
"session_id": {
"description": "Session identifier for session_content action",
"type": "string"
}
},
"required": [
"action"
],
"type": "object"
},
"annotations": {
"title": "Manage scheduled recipes",
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": false
}
},
{
"name": "subagent__execute_task",
"description": "Only use the subagent__execute_task tool when you execute sub recipe task or dynamic task.\n EXECUTION STRATEGY DECISION:\n 1. If the tasks are created with execution_mode, use the execution_mode.\n 2. Execute tasks sequentially unless user explicitly requests parallel execution. PARALLEL: User uses keywords like 'parallel', 'simultaneously', 'at the same time', 'concurrently'\n\n IMPLEMENTATION:\n - Sequential execution: Call this tool multiple times, passing exactly ONE task per call\n - Parallel execution: Call this tool once, passing an ARRAY of all tasks\n\n EXAMPLES:\n User Intent Based:\n - User: 'get weather and tell me a joke' → Sequential (2 separate tool calls, 1 task each)\n - User: 'get weather and joke in parallel' → Parallel (1 tool call with array of 2 tasks)\n - User: 'run these simultaneously' → Parallel (1 tool call with task array)\n - User: 'do task A then task B' → Sequential (2 separate tool calls)",
"inputSchema": {
"properties": {
"execution_mode": {
"default": "sequential",
"description": "Execution strategy for multiple tasks. Use 'sequential' (default) unless user explicitly requests parallel execution with words like 'parallel', 'simultaneously', 'at the same time', or 'concurrently'.",
"enum": [
"sequential",
"parallel"
],
"type": "string"
},
"task_ids": {
"items": {
"description": "Unique identifier for the task",
"type": "string"
},
"type": "array"
}
},
"required": [
"task_ids"
],
"type": "object"
},
"annotations": {
"title": "Run tasks in parallel",
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": true
}
},
{
"name": "weather_extension__get_weather",
"description": "Get the weather for a location",
"inputSchema": {
"properties": {
"location": {
"description": "The city and state, e.g. San Francisco, CA",
"type": "string"
}
},
"required": [
"location"
],
"type": "object"
}
}
]
},
"output": {
"message": {
"id": null,
"role": "assistant",
"created": 1763989814,
"content": [
{
"type": "toolRequest",
"id": "4kpg6v08d",
"toolCall": {
"status": "success",
"value": {
"name": "weather_extension__get_weather",
"arguments": {
"location": "Berlin, Germany"
}
}
}
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
},
"usage": {
"model": "llama-3.3-70b-versatile",
"usage": {
"input_tokens": 2258,
"output_tokens": 20,
"total_tokens": 2278
}
}
}
},
"165258e98e56cdcccdc49bd70ecd2b081b41d35ddb3559ed3bc7b1e420290bed": {
"input": {
"system": "You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.\ngoose is being developed as an open-source software project.\n\ngoose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,\nclaude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).\nThese models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10\nmonths prior to the current date.\n\n# Extensions\n\nExtensions allow other applications to provide context to goose. Extensions connect goose to different data sources and\ntools.\nYou are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level\nproblems using the tools in these extensions, and can interact with multiple at once.\n\nIf the Extension Manager extension is enabled, you can use the search_available_extensions tool to discover additional\nextensions that can help with your task. To enable or disable extensions, use the manage_extensions tool with the\nextension_name. You should only enable extensions found from the search_available_extensions tool.\nIf Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load\nnew ones.\n\nBecause you dynamically load extensions, your conversation history may refer\nto interactions with extensions that are not currently active. The currently\nactive extensions are below. Each of these extensions provides tools that are\nin your tool specification.\n\n\n## weather_extension\n\n\n\n\n\n\n# Response Guidelines\n\n- Use Markdown formatting for all responses.\n- Follow best practices for Markdown, including:\n - Using headers for organization.\n - Bullet points for lists.\n - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic\n links using angle brackets (e.g., <http://example.com/>).\n- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the\n language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting.\n- Ensure clarity, conciseness, and proper formatting to enhance readability and usability.",
"messages": [
{
"id": "msg_20251124_10_0",
"role": "user",
"created": 1763989814,
"created": 1765495334,
"content": [
{
"type": "text",
@@ -308,13 +19,13 @@
}
},
{
"id": "msg_9d25f6e9-6d02-4f41-8710-325795e87fbc",
"id": "msg_6147fd68-5a38-4528-9924-d4dbaf5e7f44",
"role": "assistant",
"created": 1763989814,
"created": 1765495335,
"content": [
{
"type": "toolRequest",
"id": "4kpg6v08d",
"id": "zemx6n94z",
"toolCall": {
"status": "success",
"value": {
@@ -332,21 +43,23 @@
}
},
{
"id": "msg_513af900-f4fe-4ed0-aed8-7ffb4c8c384d",
"id": "msg_0fac763c-62c3-4a33-a15b-27d605c5b81d",
"role": "user",
"created": 1763989814,
"created": 1765495335,
"content": [
{
"type": "toolResponse",
"id": "4kpg6v08d",
"id": "zemx6n94z",
"toolResult": {
"status": "success",
"value": [
{
"type": "text",
"text": "The weather in Berlin, Germany is cloudy and 18°C"
}
]
"value": {
"content": [
{
"type": "text",
"text": "The weather in Berlin, Germany is cloudy and 18°C"
}
]
}
}
}
],
@@ -594,7 +307,7 @@
"message": {
"id": null,
"role": "assistant",
"created": 1763989815,
"created": 1765495335,
"content": [
{
"type": "text",
@@ -623,7 +336,7 @@
{
"id": null,
"role": "user",
"created": 1763989814,
"created": 1765495334,
"content": [
{
"type": "text",
@@ -642,11 +355,11 @@
"message": {
"id": null,
"role": "assistant",
"created": 1763989814,
"created": 1765495334,
"content": [
{
"type": "text",
"text": "Berlin weather query"
"text": "Berlin weather"
}
],
"metadata": {
@@ -658,8 +371,297 @@
"model": "llama-3.3-70b-versatile",
"usage": {
"input_tokens": 108,
"output_tokens": 4,
"total_tokens": 112
"output_tokens": 3,
"total_tokens": 111
}
}
}
},
"1bc400a528c54b25f4f1f609481e98e44222b3deaf7eee2c9e640e6345c73861": {
"input": {
"system": "You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.\ngoose is being developed as an open-source software project.\n\ngoose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,\nclaude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).\nThese models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10\nmonths prior to the current date.\n\n# Extensions\n\nExtensions allow other applications to provide context to goose. Extensions connect goose to different data sources and\ntools.\nYou are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level\nproblems using the tools in these extensions, and can interact with multiple at once.\n\nIf the Extension Manager extension is enabled, you can use the search_available_extensions tool to discover additional\nextensions that can help with your task. To enable or disable extensions, use the manage_extensions tool with the\nextension_name. You should only enable extensions found from the search_available_extensions tool.\nIf Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load\nnew ones.\n\nBecause you dynamically load extensions, your conversation history may refer\nto interactions with extensions that are not currently active. The currently\nactive extensions are below. Each of these extensions provides tools that are\nin your tool specification.\n\n\n## weather_extension\n\n\n\n\n\n\n# Response Guidelines\n\n- Use Markdown formatting for all responses.\n- Follow best practices for Markdown, including:\n - Using headers for organization.\n - Bullet points for lists.\n - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic\n links using angle brackets (e.g., <http://example.com/>).\n- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the\n language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting.\n- Ensure clarity, conciseness, and proper formatting to enhance readability and usability.",
"messages": [
{
"id": "msg_20251211_40_0",
"role": "user",
"created": 1765495334,
"content": [
{
"type": "text",
"text": "tell me what the weather is in Berlin, Germany"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
}
],
"tools": [
{
"name": "dynamic_task__create_task",
"description": "Create tasks with instructions or prompt. For simple tasks, only include the instructions field. Extensions control: omit field = use all current extensions; empty array [] = no extensions; array with names = only those extensions. Specify extensions as shortnames (the prefixes for your tools). Specify return_last_only as true and have your subagent summarize its work in its last message to conserve your own context. Optional: title, description, extensions, settings, retry, response schema, activities. Arrays for multiple tasks.",
"inputSchema": {
"$defs": {
"TaskParameter": {
"description": "Parameters for a single task",
"properties": {
"activities": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"description": {
"type": [
"string",
"null"
]
},
"extensions": {
"items": {
"additionalProperties": true,
"type": "object"
},
"type": [
"array",
"null"
]
},
"instructions": {
"type": [
"string",
"null"
]
},
"parameters": {
"items": {
"additionalProperties": true,
"type": "object"
},
"type": [
"array",
"null"
]
},
"prompt": {
"type": [
"string",
"null"
]
},
"response": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"retry": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"return_last_only": {
"description": "If true, return only the last message from the subagent (default: false, returns full conversation)",
"type": [
"boolean",
"null"
]
},
"settings": {
"additionalProperties": true,
"type": [
"object",
"null"
]
},
"title": {
"type": [
"string",
"null"
]
}
},
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"execution_mode": {
"description": "How to execute multiple tasks (default: parallel for multiple tasks, sequential for single task)",
"type": [
"string",
"null"
]
},
"task_parameters": {
"description": "Array of tasks. Each task must have either 'instructions' OR 'prompt' field (at least one is required).",
"items": {
"$ref": "#/$defs/TaskParameter"
},
"minItems": 1,
"type": "array"
}
},
"required": [
"task_parameters"
],
"title": "CreateDynamicTaskParams",
"type": "object"
},
"annotations": {
"title": "Create Dynamic Tasks",
"readOnlyHint": false,
"destructiveHint": false,
"idempotentHint": false,
"openWorldHint": true
}
},
{
"name": "platform__manage_schedule",
"description": "Manage scheduled recipe execution for this goose instance.\n\nActions:\n- \"list\": List all scheduled jobs\n- \"create\": Create a new scheduled job from a recipe file\n- \"run_now\": Execute a scheduled job immediately \n- \"pause\": Pause a scheduled job\n- \"unpause\": Resume a paused job\n- \"delete\": Remove a scheduled job\n- \"kill\": Terminate a currently running job\n- \"inspect\": Get details about a running job\n- \"sessions\": List execution history for a job\n- \"session_content\": Get the full content (messages) of a specific session\n",
"inputSchema": {
"properties": {
"action": {
"enum": [
"list",
"create",
"run_now",
"pause",
"unpause",
"delete",
"kill",
"inspect",
"sessions",
"session_content"
],
"type": "string"
},
"cron_expression": {
"description": "A cron expression for create action. Supports both 5-field (minute hour day month weekday) and 6-field (second minute hour day month weekday) formats. 5-field expressions are automatically converted to 6-field by prepending '0' for seconds.",
"type": "string"
},
"job_id": {
"description": "Job identifier for operations on existing jobs",
"type": "string"
},
"limit": {
"default": 50,
"description": "Limit for sessions list",
"type": "integer"
},
"recipe_path": {
"description": "Path to recipe file for create action",
"type": "string"
},
"session_id": {
"description": "Session identifier for session_content action",
"type": "string"
}
},
"required": [
"action"
],
"type": "object"
},
"annotations": {
"title": "Manage scheduled recipes",
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": false
}
},
{
"name": "subagent__execute_task",
"description": "Only use the subagent__execute_task tool when you execute sub recipe task or dynamic task.\n EXECUTION STRATEGY DECISION:\n 1. If the tasks are created with execution_mode, use the execution_mode.\n 2. Execute tasks sequentially unless user explicitly requests parallel execution. PARALLEL: User uses keywords like 'parallel', 'simultaneously', 'at the same time', 'concurrently'\n\n IMPLEMENTATION:\n - Sequential execution: Call this tool multiple times, passing exactly ONE task per call\n - Parallel execution: Call this tool once, passing an ARRAY of all tasks\n\n EXAMPLES:\n User Intent Based:\n - User: 'get weather and tell me a joke' → Sequential (2 separate tool calls, 1 task each)\n - User: 'get weather and joke in parallel' → Parallel (1 tool call with array of 2 tasks)\n - User: 'run these simultaneously' → Parallel (1 tool call with task array)\n - User: 'do task A then task B' → Sequential (2 separate tool calls)",
"inputSchema": {
"properties": {
"execution_mode": {
"default": "sequential",
"description": "Execution strategy for multiple tasks. Use 'sequential' (default) unless user explicitly requests parallel execution with words like 'parallel', 'simultaneously', 'at the same time', or 'concurrently'.",
"enum": [
"sequential",
"parallel"
],
"type": "string"
},
"task_ids": {
"items": {
"description": "Unique identifier for the task",
"type": "string"
},
"type": "array"
}
},
"required": [
"task_ids"
],
"type": "object"
},
"annotations": {
"title": "Run tasks in parallel",
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": false,
"openWorldHint": true
}
},
{
"name": "weather_extension__get_weather",
"description": "Get the weather for a location",
"inputSchema": {
"properties": {
"location": {
"description": "The city and state, e.g. San Francisco, CA",
"type": "string"
}
},
"required": [
"location"
],
"type": "object"
}
}
]
},
"output": {
"message": {
"id": null,
"role": "assistant",
"created": 1765495335,
"content": [
{
"type": "toolRequest",
"id": "zemx6n94z",
"toolCall": {
"status": "success",
"value": {
"name": "weather_extension__get_weather",
"arguments": {
"location": "Berlin, Germany"
}
}
}
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
},
"usage": {
"model": "llama-3.3-70b-versatile",
"usage": {
"input_tokens": 2258,
"output_tokens": 20,
"total_tokens": 2278
}
}
}
@@ -4,9 +4,9 @@
"system": "You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.\ngoose is being developed as an open-source software project.\n\ngoose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,\nclaude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).\nThese models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10\nmonths prior to the current date.\n\n# Extensions\n\nExtensions allow other applications to provide context to goose. Extensions connect goose to different data sources and\ntools.\nYou are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level\nproblems using the tools in these extensions, and can interact with multiple at once.\n\nIf the Extension Manager extension is enabled, you can use the search_available_extensions tool to discover additional\nextensions that can help with your task. To enable or disable extensions, use the manage_extensions tool with the\nextension_name. You should only enable extensions found from the search_available_extensions tool.\nIf Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load\nnew ones.\n\nBecause you dynamically load extensions, your conversation history may refer\nto interactions with extensions that are not currently active. The currently\nactive extensions are below. Each of these extensions provides tools that are\nin your tool specification.\n\n\n## weather_extension\n\n\n\n\n\n\n# Response Guidelines\n\n- Use Markdown formatting for all responses.\n- Follow best practices for Markdown, including:\n - Using headers for organization.\n - Bullet points for lists.\n - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic\n links using angle brackets (e.g., <http://example.com/>).\n- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the\n language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting.\n- Ensure clarity, conciseness, and proper formatting to enhance readability and usability.",
"messages": [
{
"id": "msg_20251124_7_0",
"id": "msg_20251211_26_0",
"role": "user",
"created": 1763989801,
"created": 1765495261,
"content": [
{
"type": "text",
@@ -257,11 +257,11 @@
"message": {
"id": null,
"role": "assistant",
"created": 1763989803,
"created": 1765495262,
"content": [
{
"type": "toolRequest",
"id": "call_GmIN2vOnjDg6MSVWWFuZ4FlV",
"id": "call_sVLij6GavUkUc5Ri4VeDdgmN",
"toolCall": {
"status": "success",
"value": {
@@ -288,62 +288,14 @@
}
}
},
"8b1b8633232ca390cb3ff37a48daf74168b58f13e2943efcb17957129ee34d1a": {
"input": {
"system": "Reply with only a description in four words or less",
"messages": [
{
"id": null,
"role": "user",
"created": 1763989801,
"content": [
{
"type": "text",
"text": "Here are the first few user messages:\ntell me what the weather is in Berlin, Germany\n\nBased on the conversation so far, provide a concise description of this session in 4 words or less. This will be used for finding the session later in a UI with limited space - reply *ONLY* with the description"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
}
],
"tools": []
},
"output": {
"message": {
"id": null,
"role": "assistant",
"created": 1763989804,
"content": [
{
"type": "text",
"text": "Berlin weather inquiry"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
},
"usage": {
"model": "gpt-4o-2024-08-06",
"usage": {
"input_tokens": 84,
"output_tokens": 3,
"total_tokens": 87
}
}
}
},
"98726e7698e93241dfcba182bdca3a7d0640f0f538f086f4981dca6689b7ab5a": {
"950a6b5ec929abdd9e7cd337d2ea80c07631c23c6d64edac31988e18c6207e92": {
"input": {
"system": "You are a general-purpose AI agent called goose, created by Block, the parent company of Square, CashApp, and Tidal.\ngoose is being developed as an open-source software project.\n\ngoose uses LLM providers with tool calling capability. You can be used with different language models (gpt-4o,\nclaude-sonnet-4, o1, llama-3.2, deepseek-r1, etc).\nThese models have varying knowledge cut-off dates depending on when they were trained, but typically it's between 5-10\nmonths prior to the current date.\n\n# Extensions\n\nExtensions allow other applications to provide context to goose. Extensions connect goose to different data sources and\ntools.\nYou are capable of dynamically plugging into new extensions and learning how to use them. You solve higher level\nproblems using the tools in these extensions, and can interact with multiple at once.\n\nIf the Extension Manager extension is enabled, you can use the search_available_extensions tool to discover additional\nextensions that can help with your task. To enable or disable extensions, use the manage_extensions tool with the\nextension_name. You should only enable extensions found from the search_available_extensions tool.\nIf Extension Manager is not available, you can only work with currently enabled extensions and cannot dynamically load\nnew ones.\n\nBecause you dynamically load extensions, your conversation history may refer\nto interactions with extensions that are not currently active. The currently\nactive extensions are below. Each of these extensions provides tools that are\nin your tool specification.\n\n\n## weather_extension\n\n\n\n\n\n\n# Response Guidelines\n\n- Use Markdown formatting for all responses.\n- Follow best practices for Markdown, including:\n - Using headers for organization.\n - Bullet points for lists.\n - Links formatted correctly, either as linked text (e.g., [this is linked text](https://example.com)) or automatic\n links using angle brackets (e.g., <http://example.com/>).\n- For code examples, use fenced code blocks by placing triple backticks (` ``` `) before and after the code. Include the\n language identifier after the opening backticks (e.g., ` ```python `) to enable syntax highlighting.\n- Ensure clarity, conciseness, and proper formatting to enhance readability and usability.",
"messages": [
{
"id": "msg_20251124_7_0",
"id": "msg_20251211_26_0",
"role": "user",
"created": 1763989801,
"created": 1765495261,
"content": [
{
"type": "text",
@@ -356,13 +308,13 @@
}
},
{
"id": "msg_9a0fb3d7-d681-40b9-ab58-bb1cbe03e4b0",
"id": "msg_899ef38b-1440-47f7-a942-1b2e58c8835c",
"role": "assistant",
"created": 1763989803,
"created": 1765495262,
"content": [
{
"type": "toolRequest",
"id": "call_GmIN2vOnjDg6MSVWWFuZ4FlV",
"id": "call_sVLij6GavUkUc5Ri4VeDdgmN",
"toolCall": {
"status": "success",
"value": {
@@ -380,21 +332,23 @@
}
},
{
"id": "msg_51ae1986-b095-4163-8d42-004ff1a28c59",
"id": "msg_2f8ddf2e-dedb-4b99-9549-826796292622",
"role": "user",
"created": 1763989803,
"created": 1765495262,
"content": [
{
"type": "toolResponse",
"id": "call_GmIN2vOnjDg6MSVWWFuZ4FlV",
"id": "call_sVLij6GavUkUc5Ri4VeDdgmN",
"toolResult": {
"status": "success",
"value": [
{
"type": "text",
"text": "The weather in Berlin, Germany is cloudy and 18°C"
}
]
"value": {
"content": [
{
"type": "text",
"text": "The weather in Berlin, Germany is cloudy and 18°C"
}
]
}
}
}
],
@@ -642,7 +596,7 @@
"message": {
"id": null,
"role": "assistant",
"created": 1763989805,
"created": 1765495263,
"content": [
{
"type": "text",
@@ -663,5 +617,53 @@
}
}
}
},
"8b1b8633232ca390cb3ff37a48daf74168b58f13e2943efcb17957129ee34d1a": {
"input": {
"system": "Reply with only a description in four words or less",
"messages": [
{
"id": null,
"role": "user",
"created": 1765495261,
"content": [
{
"type": "text",
"text": "Here are the first few user messages:\ntell me what the weather is in Berlin, Germany\n\nBased on the conversation so far, provide a concise description of this session in 4 words or less. This will be used for finding the session later in a UI with limited space - reply *ONLY* with the description"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
}
],
"tools": []
},
"output": {
"message": {
"id": null,
"role": "assistant",
"created": 1765495262,
"content": [
{
"type": "text",
"text": "Berlin weather inquiry"
}
],
"metadata": {
"userVisible": true,
"agentVisible": true
}
},
"usage": {
"model": "gpt-4o-2024-08-06",
"usage": {
"input_tokens": 84,
"output_tokens": 3,
"total_tokens": 87
}
}
}
}
}
+87 -17
View File
@@ -221,12 +221,12 @@ pub fn tool_response_to_markdown(resp: &ToolResponse, export_all_content: bool)
md.push_str("#### Tool Response:\n");
match &resp.tool_result {
Ok(contents) => {
if contents.is_empty() {
Ok(result) => {
if result.content.is_empty() {
md.push_str("*No textual output from tool.*\n");
}
for content in contents {
for content in &result.content {
if !export_all_content {
if let Some(audience) = content.audience() {
if !audience.contains(&Role::Assistant) {
@@ -581,7 +581,12 @@ mod tests {
};
let tool_response = ToolResponse {
id: "test-id".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let result = tool_response_to_markdown(&tool_response, true);
@@ -601,7 +606,12 @@ mod tests {
};
let tool_response = ToolResponse {
id: "test-id".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let result = tool_response_to_markdown(&tool_response, true);
@@ -707,7 +717,12 @@ if __name__ == "__main__":
};
let tool_response = ToolResponse {
id: "shell-cat".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let request_result = tool_request_to_markdown(&tool_request, true);
@@ -748,7 +763,12 @@ if __name__ == "__main__":
};
let tool_response = ToolResponse {
id: "git-status".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let request_result = tool_request_to_markdown(&tool_request, true);
@@ -797,7 +817,12 @@ warning: unused variable `x`
};
let tool_response = ToolResponse {
id: "cargo-build".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let response_result = tool_response_to_markdown(&tool_response, true);
@@ -844,7 +869,12 @@ warning: unused variable `x`
};
let tool_response = ToolResponse {
id: "curl-api".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let response_result = tool_response_to_markdown(&tool_response, true);
@@ -880,7 +910,12 @@ warning: unused variable `x`
};
let tool_response = ToolResponse {
id: "editor-write".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let request_result = tool_request_to_markdown(&tool_request, true);
@@ -937,7 +972,12 @@ def process_data(data: List[Dict]) -> List[Dict]:
};
let tool_response = ToolResponse {
id: "editor-view".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let response_result = tool_response_to_markdown(&tool_response, true);
@@ -974,7 +1014,12 @@ Command failed with exit code 2"#;
};
let tool_response = ToolResponse {
id: "shell-error".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let response_result = tool_response_to_markdown(&tool_response, true);
@@ -1014,7 +1059,12 @@ Command failed with exit code 2"#;
};
let tool_response = ToolResponse {
id: "script-exec".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let request_result = tool_request_to_markdown(&tool_request, true);
@@ -1061,7 +1111,12 @@ drwx------ 3 user staff 96 Dec 6 16:20 com.apple.launchd.abc
};
let tool_response = ToolResponse {
id: "multi-cmd".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let request_result = tool_request_to_markdown(&_tool_request, true);
@@ -1104,7 +1159,12 @@ src/middleware.rs:12:async fn auth_middleware(req: Request, next: Next) -> Resul
};
let tool_response = ToolResponse {
id: "grep-search".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let request_result = tool_request_to_markdown(&tool_request, true);
@@ -1144,7 +1204,12 @@ src/middleware.rs:12:async fn auth_middleware(req: Request, next: Next) -> Resul
};
let tool_response = ToolResponse {
id: "json-test".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let response_result = tool_response_to_markdown(&tool_response, true);
@@ -1185,7 +1250,12 @@ found 0 vulnerabilities"#;
};
let tool_response = ToolResponse {
id: "npm-install".to_string(),
tool_result: Ok(vec![Content::text(text_content.raw.text)]),
tool_result: Ok(rmcp::model::CallToolResult {
content: vec![Content::text(text_content.raw.text)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
};
let request_result = tool_request_to_markdown(&tool_request, true);
+2 -2
View File
@@ -285,8 +285,8 @@ fn render_tool_response(resp: &ToolResponse, theme: Theme, debug: bool) {
let config = Config::global();
match &resp.tool_result {
Ok(contents) => {
for content in contents {
Ok(result) => {
for content in &result.content {
if let Some(audience) = content.audience() {
if !audience.contains(&rmcp::model::Role::User) {
continue;
+6
View File
@@ -354,6 +354,8 @@ derive_utoipa!(Icon as IconSchema);
super::routes::agent::start_agent,
super::routes::agent::resume_agent,
super::routes::agent::get_tools,
super::routes::agent::read_resource,
super::routes::agent::call_tool,
super::routes::agent::update_from_session,
super::routes::agent::agent_add_extension,
super::routes::agent::agent_remove_extension,
@@ -514,6 +516,10 @@ derive_utoipa!(Icon as IconSchema);
goose::agents::types::SuccessCheck,
super::routes::agent::UpdateProviderRequest,
super::routes::agent::GetToolsQuery,
super::routes::agent::ReadResourceRequest,
super::routes::agent::ReadResourceResponse,
super::routes::agent::CallToolRequest,
super::routes::agent::CallToolResponse,
super::routes::agent::UpdateRouterToolSelectorRequest,
super::routes::agent::StartAgentRequest,
super::routes::agent::ResumeAgentRequest,
+113 -2
View File
@@ -25,12 +25,14 @@ use goose::{
agents::{extension::ToolInfo, extension_manager::get_parameter_names},
config::permission::PermissionLevel,
};
use serde::Deserialize;
use rmcp::model::{CallToolRequestParam, Content};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use tokio_util::sync::CancellationToken;
use tracing::{error, warn};
#[derive(Deserialize, utoipa::ToSchema)]
@@ -90,6 +92,32 @@ pub struct RemoveExtensionRequest {
session_id: String,
}
#[derive(Deserialize, utoipa::ToSchema)]
pub struct ReadResourceRequest {
session_id: String,
extension_name: String,
uri: String,
}
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
pub struct ReadResourceResponse {
html: String,
}
#[derive(Deserialize, utoipa::ToSchema)]
pub struct CallToolRequest {
session_id: String,
name: String,
arguments: Value,
}
#[derive(Serialize, utoipa::ToSchema)]
pub struct CallToolResponse {
content: Vec<Content>,
structured_content: Option<Value>,
is_error: bool,
}
#[utoipa::path(
post,
path = "/agent/start",
@@ -484,7 +512,7 @@ async fn update_router_tool_selector(
.update_router_tool_selector(None, Some(true))
.await
.map_err(|e| {
tracing::error!("Failed to update tool selection strategy: {}", e);
error!("Failed to update tool selection strategy: {}", e);
StatusCode::INTERNAL_SERVER_ERROR
})?;
@@ -564,11 +592,94 @@ async fn stop_agent(
Ok(StatusCode::OK)
}
#[utoipa::path(
post,
path = "/agent/read_resource",
request_body = ReadResourceRequest,
responses(
(status = 200, description = "Resource read successfully", body = ReadResourceResponse),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
(status = 404, description = "Resource not found"),
(status = 500, description = "Internal server error")
)
)]
async fn read_resource(
State(state): State<Arc<AppState>>,
Json(payload): Json<ReadResourceRequest>,
) -> Result<Json<ReadResourceResponse>, StatusCode> {
let agent = state
.get_agent_for_route(payload.session_id.clone())
.await?;
let html = agent
.extension_manager
.read_ui_resource(
&payload.uri,
&payload.extension_name,
CancellationToken::default(),
)
.await
.map_err(|_e| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(ReadResourceResponse { html }))
}
#[utoipa::path(
post,
path = "/agent/call_tool",
request_body = CallToolRequest,
responses(
(status = 200, description = "Resource read successfully", body = CallToolResponse),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
(status = 404, description = "Resource not found"),
(status = 500, description = "Internal server error")
)
)]
async fn call_tool(
State(state): State<Arc<AppState>>,
Json(payload): Json<CallToolRequest>,
) -> Result<Json<CallToolResponse>, StatusCode> {
let agent = state
.get_agent_for_route(payload.session_id.clone())
.await?;
let arguments = match payload.arguments {
Value::Object(map) => Some(map),
_ => None,
};
let tool_call = CallToolRequestParam {
name: payload.name.into(),
arguments,
};
let tool_result = agent
.extension_manager
.dispatch_tool_call(tool_call, CancellationToken::default())
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let result = tool_result
.result
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(CallToolResponse {
content: result.content,
structured_content: result.structured_content,
is_error: result.is_error.unwrap_or(false),
}))
}
pub fn routes(state: Arc<AppState>) -> Router {
Router::new()
.route("/agent/start", post(start_agent))
.route("/agent/resume", post(resume_agent))
.route("/agent/tools", get(get_tools))
.route("/agent/read_resource", post(read_resource))
.route("/agent/call_tool", post(call_tool))
.route("/agent/update_provider", post(update_agent_provider))
.route(
"/agent/update_router_tool_selector",
+7
View File
@@ -3,6 +3,7 @@ use axum::{
response::{IntoResponse, Response},
Json,
};
use goose::config::ConfigError;
use serde::Serialize;
use utoipa::ToSchema;
@@ -37,3 +38,9 @@ impl From<anyhow::Error> for ErrorResponse {
Self::internal(err.to_string())
}
}
impl From<ConfigError> for ErrorResponse {
fn from(err: ConfigError) -> Self {
Self::internal(err.to_string())
}
}
+9 -2
View File
@@ -37,8 +37,15 @@ async fn main() -> Result<()> {
arguments: Some(object!({"path": "./test_image.png"})),
}),
),
Message::user()
.with_tool_response("000", Ok(vec![Content::image(base64_image, "image/png")])),
Message::user().with_tool_response(
"000",
Ok(rmcp::model::CallToolResult {
content: vec![Content::image(base64_image, "image/png")],
structured_content: None,
is_error: Some(false),
meta: None,
}),
),
];
// Get a response from the model about the image
+25 -8
View File
@@ -58,7 +58,7 @@ use crate::tool_monitor::RepetitionInspector;
use crate::utils::is_token_cancelled;
use regex::Regex;
use rmcp::model::{
CallToolRequestParam, Content, ErrorCode, ErrorData, GetPromptResult, Prompt,
CallToolRequestParam, CallToolResult, Content, ErrorCode, ErrorData, GetPromptResult, Prompt,
ServerNotification, Tool,
};
use serde_json::Value;
@@ -100,7 +100,7 @@ pub struct Agent {
pub(super) prompt_manager: Mutex<PromptManager>,
pub(super) confirmation_tx: mpsc::Sender<(String, PermissionConfirmation)>,
pub(super) confirmation_rx: Mutex<mpsc::Receiver<(String, PermissionConfirmation)>>,
pub(super) tool_result_tx: mpsc::Sender<(String, ToolResult<Vec<Content>>)>,
pub(super) tool_result_tx: mpsc::Sender<(String, ToolResult<CallToolResult>)>,
pub(super) tool_result_rx: ToolResultReceiver,
pub tool_route_manager: Arc<ToolRouteManager>,
@@ -128,7 +128,8 @@ pub enum ToolStreamItem<T> {
Result(T),
}
pub type ToolStream = Pin<Box<dyn Stream<Item = ToolStreamItem<ToolResult<Vec<Content>>>> + Send>>;
pub type ToolStream =
Pin<Box<dyn Stream<Item = ToolStreamItem<ToolResult<CallToolResult>>> + Send>>;
// tool_stream combines a stream of ServerNotifications with a future representing the
// final result of the tool call. MCP notifications are not request-scoped, but
@@ -137,7 +138,7 @@ pub type ToolStream = Pin<Box<dyn Stream<Item = ToolStreamItem<ToolResult<Vec<Co
pub fn tool_stream<S, F>(rx: S, done: F) -> ToolStream
where
S: Stream<Item = ServerNotification> + Send + Unpin + 'static,
F: Future<Output = ToolResult<Vec<Content>>> + Send + 'static,
F: Future<Output = ToolResult<CallToolResult>> + Send + 'static,
{
Box::pin(async_stream::stream! {
tokio::pin!(done);
@@ -360,7 +361,12 @@ impl Agent {
let mut response = response_msg.lock().await;
*response = response.clone().with_tool_response(
request.id.clone(),
Ok(vec![rmcp::model::Content::text(DECLINED_RESPONSE)]),
Ok(CallToolResult {
content: vec![rmcp::model::Content::text(DECLINED_RESPONSE)],
structured_content: None,
is_error: Some(true),
meta: None,
}),
);
}
}
@@ -454,7 +460,13 @@ impl Agent {
let result = self
.handle_schedule_management(arguments, request_id.clone())
.await;
return (request_id, Ok(ToolCallResult::from(result)));
let wrapped_result = result.map(|content| CallToolResult {
content,
structured_content: None,
is_error: Some(false),
meta: None,
});
return (request_id, Ok(ToolCallResult::from(wrapped_result)));
}
if tool_call.name == FINAL_OUTPUT_TOOL_NAME {
@@ -1090,7 +1102,12 @@ impl Agent {
let mut response = response_msg.lock().await;
*response = response.clone().with_tool_response(
request.id.clone(),
Ok(vec![Content::text(CHAT_MODE_TOOL_SKIPPED_RESPONSE)]),
Ok(CallToolResult {
content: vec![Content::text(CHAT_MODE_TOOL_SKIPPED_RESPONSE)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
);
}
}
@@ -1421,7 +1438,7 @@ impl Agent {
Ok(plan_prompt)
}
pub async fn handle_tool_result(&self, id: String, result: ToolResult<Vec<Content>>) {
pub async fn handle_tool_result(&self, id: String, result: ToolResult<CallToolResult>) {
if let Err(e) = self.tool_result_tx.send((id, result)).await {
error!("Failed to send tool result: {}", e);
}
+74 -8
View File
@@ -41,8 +41,8 @@ use crate::oauth::oauth_flow;
use crate::prompt_template;
use crate::subprocess::configure_command_no_window;
use rmcp::model::{
CallToolRequestParam, Content, ErrorCode, ErrorData, GetPromptResult, Prompt, ResourceContents,
ServerInfo, Tool,
CallToolRequestParam, Content, ErrorCode, ErrorData, GetPromptResult, Prompt, RawContent,
Resource, ResourceContents, ServerInfo, Tool,
};
use rmcp::transport::auth::AuthClient;
use schemars::_private::NoSerialize;
@@ -758,6 +758,7 @@ impl ExtensionManager {
uri,
extension_name.unwrap(),
cancellation_token.clone(),
true,
)
.await?;
return Ok(result);
@@ -773,7 +774,12 @@ impl ExtensionManager {
for extension_name in extension_names {
let result = self
.read_resource_from_extension(uri, &extension_name, cancellation_token.clone())
.read_resource_from_extension(
uri,
&extension_name,
cancellation_token.clone(),
true,
)
.await;
match result {
Ok(result) => return Ok(result),
@@ -807,6 +813,7 @@ impl ExtensionManager {
uri: &str,
extension_name: &str,
cancellation_token: CancellationToken,
format_with_uri: bool,
) -> Result<Vec<Content>, ErrorData> {
let available_extensions = self
.extensions
@@ -840,9 +847,12 @@ impl ExtensionManager {
let mut result = Vec::new();
for content in read_result.contents {
// Only reading the text resource content; skipping the blob content cause it's too long
if let ResourceContents::TextResourceContents { text, .. } = content {
let content_str = format!("{}\n\n{}", uri, text);
let content_str = if format_with_uri {
format!("{}\n\n{}", uri, text)
} else {
text
};
result.push(Content::text(content_str));
}
}
@@ -850,6 +860,65 @@ impl ExtensionManager {
Ok(result)
}
pub async fn get_ui_resources(&self) -> Result<Vec<(String, Resource)>, ErrorData> {
let mut ui_resources = Vec::new();
let extensions_to_check: Vec<(String, McpClientBox)> = {
let extensions = self.extensions.lock().await;
extensions
.iter()
.map(|(name, ext)| (name.clone(), ext.get_client()))
.collect()
};
for (extension_name, client) in extensions_to_check {
let client_guard = client.lock().await;
match client_guard
.list_resources(None, CancellationToken::default())
.await
{
Ok(list_response) => {
for resource in list_response.resources {
if resource.uri.starts_with("ui://") {
ui_resources.push((extension_name.clone(), resource));
}
}
}
Err(e) => {
warn!("Failed to list resources for {}: {:?}", extension_name, e);
}
}
}
Ok(ui_resources)
}
pub async fn read_ui_resource(
&self,
uri: &str,
extension_name: &str,
cancellation_token: CancellationToken,
) -> Result<String, ErrorData> {
let contents = self
.read_resource_from_extension(uri, extension_name, cancellation_token, false)
.await?;
contents
.into_iter()
.find_map(|c| match c.raw {
RawContent::Text(text_content) => Some(text_content.text),
_ => None,
})
.ok_or_else(|| {
ErrorData::new(
ErrorCode::RESOURCE_NOT_FOUND,
format!("No text content in resource '{}'", uri),
None,
)
})
}
async fn list_resources_from_extension(
&self,
extension_name: &str,
@@ -936,7 +1005,6 @@ impl ExtensionManager {
}
}
// Log any errors that occurred
if !errors.is_empty() {
tracing::error!(
errors = ?errors
@@ -998,7 +1066,6 @@ impl ExtensionManager {
client_guard
.call_tool(&tool_name, arguments, cancellation_token)
.await
.map(|call| call.content)
.map_err(|e| match e {
ServiceError::McpError(error_data) => error_data,
_ => {
@@ -1077,7 +1144,6 @@ impl ExtensionManager {
}
}
// Log any errors that occurred
if !errors.is_empty() {
tracing::debug!(
errors = ?errors
+8 -3
View File
@@ -123,9 +123,14 @@ impl FinalOutputTool {
match result {
Ok(parsed_value) => {
self.final_output = Some(Self::parsed_final_output_string(parsed_value));
ToolCallResult::from(Ok(vec![Content::text(
"Final output successfully collected.".to_string(),
)]))
ToolCallResult::from(Ok(rmcp::model::CallToolResult {
content: vec![Content::text(
"Final output successfully collected.".to_string(),
)],
structured_content: None,
is_error: Some(false),
meta: None,
}))
}
Err(error) => ToolCallResult::from(Err(ErrorData {
code: ErrorCode::INVALID_PARAMS,
@@ -1,5 +1,5 @@
use chrono::Utc;
use rmcp::model::{Content, ErrorData};
use rmcp::model::{CallToolResult, Content, ErrorData};
use std::fs::File;
use std::io::Write;
@@ -7,13 +7,13 @@ const LARGE_TEXT_THRESHOLD: usize = 200_000;
/// Process tool response and handle large text content
pub fn process_tool_response(
response: Result<Vec<Content>, ErrorData>,
) -> Result<Vec<Content>, ErrorData> {
response: Result<CallToolResult, ErrorData>,
) -> Result<CallToolResult, ErrorData> {
match response {
Ok(contents) => {
Ok(mut result) => {
let mut processed_contents = Vec::new();
for content in contents {
for content in result.content {
match content.as_text() {
Some(text_content) => {
// Check if text exceeds threshold
@@ -51,7 +51,8 @@ pub fn process_tool_response(
}
}
Ok(processed_contents)
result.content = processed_contents;
Ok(result)
}
Err(e) => Err(e),
}
@@ -89,14 +90,19 @@ mod tests {
let small_text = "This is a small text response";
let content = Content::text(small_text.to_string());
let response = Ok(vec![content]);
let response = Ok(CallToolResult {
content: vec![content],
structured_content: None,
is_error: Some(false),
meta: None,
});
// Process the response
let processed = process_tool_response(response).unwrap();
// Verify the response is unchanged
assert_eq!(processed.len(), 1);
if let Some(text_content) = processed[0].as_text() {
assert_eq!(processed.content.len(), 1);
if let Some(text_content) = processed.content[0].as_text() {
assert_eq!(text_content.text, small_text);
} else {
panic!("Expected text content");
@@ -109,14 +115,19 @@ mod tests {
let large_text = "a".repeat(LARGE_TEXT_THRESHOLD + 1000);
let content = Content::text(large_text.clone());
let response = Ok(vec![content]);
let response = Ok(CallToolResult {
content: vec![content],
structured_content: None,
is_error: Some(false),
meta: None,
});
// Process the response
let processed = process_tool_response(response).unwrap();
// Verify the response contains a message about the file
assert_eq!(processed.len(), 1);
if let Some(text_content) = processed[0].as_text() {
assert_eq!(processed.content.len(), 1);
if let Some(text_content) = processed.content[0].as_text() {
assert!(text_content
.text
.contains("The response returned from the tool call was larger"));
@@ -146,14 +157,19 @@ mod tests {
// Create an image content
let image_content = Content::image("base64data".to_string(), "image/png".to_string());
let response = Ok(vec![image_content]);
let response = Ok(CallToolResult {
content: vec![image_content],
structured_content: None,
is_error: Some(false),
meta: None,
});
// Process the response
let processed = process_tool_response(response).unwrap();
// Verify the response is unchanged
assert_eq!(processed.len(), 1);
if let Some(img) = processed[0].as_image() {
assert_eq!(processed.content.len(), 1);
if let Some(img) = processed.content[0].as_image() {
assert_eq!(img.data, "base64data");
assert_eq!(img.mime_type, "image/png");
} else {
@@ -168,23 +184,28 @@ mod tests {
let large_text = Content::text("a".repeat(LARGE_TEXT_THRESHOLD + 1000));
let image = Content::image("image_data".to_string(), "image/jpeg".to_string());
let response = Ok(vec![small_text, large_text, image]);
let response = Ok(CallToolResult {
content: vec![small_text, large_text, image],
structured_content: None,
is_error: Some(false),
meta: None,
});
// Process the response
let processed = process_tool_response(response).unwrap();
// Verify each item is handled correctly
assert_eq!(processed.len(), 3);
assert_eq!(processed.content.len(), 3);
// First item should be unchanged small text
if let Some(text_content) = processed[0].as_text() {
if let Some(text_content) = processed.content[0].as_text() {
assert_eq!(text_content.text, "Small text");
} else {
panic!("Expected text content");
}
// Second item should be a message about the file
if let Some(text_content) = processed[1].as_text() {
if let Some(text_content) = processed.content[1].as_text() {
assert!(text_content
.text
.contains("The response returned from the tool call was larger"));
@@ -201,7 +222,7 @@ mod tests {
}
// Third item should be unchanged image
if let Some(img) = processed[2].as_image() {
if let Some(img) = processed.content[2].as_image() {
assert_eq!(img.data, "image_data");
assert_eq!(img.mime_type, "image/jpeg");
} else {
@@ -217,7 +238,7 @@ mod tests {
message: Cow::from("Test error"),
data: None,
};
let response: Result<Vec<Content>, ErrorData> = Err(error);
let response: Result<CallToolResult, ErrorData> = Err(error);
// Process the response
let processed = process_tool_response(response);
+18 -2
View File
@@ -106,7 +106,15 @@ mod tests {
arguments: None,
}),
),
Message::user().with_tool_response("search_1", Ok(vec![])),
Message::user().with_tool_response(
"search_1",
Ok(rmcp::model::CallToolResult {
content: vec![],
structured_content: None,
is_error: Some(false),
meta: None,
}),
),
Message::assistant()
.with_text("I need to search more")
.with_tool_request(
@@ -116,7 +124,15 @@ mod tests {
arguments: None,
}),
),
Message::user().with_tool_response("search_2", Ok(vec![])),
Message::user().with_tool_response(
"search_2",
Ok(rmcp::model::CallToolResult {
content: vec![],
structured_content: None,
is_error: Some(false),
meta: None,
}),
),
]);
let result = inject_moim(conv, &em).await;
@@ -370,5 +370,10 @@ pub async fn create_dynamic_task(
};
tasks_manager.save_tasks(tasks).await;
ToolCallResult::from(Ok(vec![Content::text(tasks_json)]))
ToolCallResult::from(Ok(rmcp::model::CallToolResult {
content: vec![Content::text(tasks_json)],
structured_content: None,
is_error: Some(false),
meta: None,
}))
}
@@ -63,7 +63,12 @@ impl SubRecipeManager {
.call_sub_recipe_tool(tool_name, params, tasks_manager, parent_working_dir)
.await;
match result {
Ok(call_result) => ToolCallResult::from(Ok(call_result)),
Ok(content) => ToolCallResult::from(Ok(rmcp::model::CallToolResult {
content,
structured_content: None,
is_error: Some(false),
meta: None,
})),
Err(e) => ToolCallResult::from(Err(ErrorData {
code: ErrorCode::INTERNAL_ERROR,
message: Cow::from(e.to_string()),
@@ -82,7 +82,12 @@ pub async fn run_tasks(
{
Ok(result) => {
let output = serde_json::to_string(&result).unwrap();
Ok(vec![Content::text(output)])
Ok(rmcp::model::CallToolResult {
content: vec![Content::text(output)],
structured_content: None,
is_error: Some(false),
meta: None,
})
}
Err(e) => Err(ErrorData {
code: ErrorCode::INTERNAL_ERROR,
+3 -2
View File
@@ -61,8 +61,9 @@ pub async fn run_complete_subagent_task(
tool_response,
) => {
// Extract text from tool response
if let Ok(contents) = &tool_response.tool_result {
let texts: Vec<String> = contents
if let Ok(result) = &tool_response.tool_result {
let texts: Vec<String> = result
.content
.iter()
.filter_map(|content| {
if let rmcp::model::RawContent::Text(raw_text_content) =
+9 -4
View File
@@ -16,12 +16,12 @@ use rmcp::model::{Content, ServerNotification};
// ToolCallResult combines the result of a tool call with an optional notification stream that
// can be used to receive notifications from the tool.
pub struct ToolCallResult {
pub result: Box<dyn Future<Output = ToolResult<Vec<Content>>> + Send + Unpin>,
pub result: Box<dyn Future<Output = ToolResult<rmcp::model::CallToolResult>> + Send + Unpin>,
pub notification_stream: Option<Box<dyn Stream<Item = ServerNotification> + Send + Unpin>>,
}
impl From<ToolResult<Vec<Content>>> for ToolCallResult {
fn from(result: ToolResult<Vec<Content>>) -> Self {
impl From<ToolResult<rmcp::model::CallToolResult>> for ToolCallResult {
fn from(result: ToolResult<rmcp::model::CallToolResult>) -> Self {
Self {
result: Box::new(futures::future::ready(result)),
notification_stream: None,
@@ -122,7 +122,12 @@ impl Agent {
let mut response = response_msg.lock().await;
*response = response.clone().with_tool_response(
request.id.clone(),
Ok(vec![Content::text(DECLINED_RESPONSE)]),
Ok(rmcp::model::CallToolResult {
content: vec![Content::text(DECLINED_RESPONSE)],
structured_content: None,
is_error: Some(true),
meta: None,
}),
);
}
}
@@ -56,7 +56,12 @@ impl ToolRouteManager {
let selector = self.router_tool_selector.lock().await.clone();
match selector.as_ref() {
Some(selector) => match selector.select_tools(arguments).await {
Ok(tools) => Ok(ToolCallResult::from(Ok(tools))),
Ok(content) => Ok(ToolCallResult::from(Ok(rmcp::model::CallToolResult {
content,
structured_content: None,
is_error: Some(false),
meta: None,
}))),
Err(e) => Err(ErrorData::new(
ErrorCode::INTERNAL_ERROR,
format!("Failed to select tools: {}", e),
+2 -2
View File
@@ -1,13 +1,13 @@
use crate::mcp_utils::ToolResult;
use crate::providers::base::Provider;
use rmcp::model::{Content, Tool};
use rmcp::model::{CallToolResult, Tool};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::{mpsc, Mutex};
use utoipa::ToSchema;
/// Type alias for the tool result channel receiver
pub type ToolResultReceiver = Arc<Mutex<mpsc::Receiver<(String, ToolResult<Vec<Content>>)>>>;
pub type ToolResultReceiver = Arc<Mutex<mpsc::Receiver<(String, ToolResult<CallToolResult>)>>>;
// We use double Arc here to allow easy provider swaps while sharing concurrent access
pub type SharedProvider = Arc<Mutex<Option<Arc<dyn Provider>>>>;
+15 -6
View File
@@ -355,8 +355,9 @@ fn format_message_for_compacting(msg: &Message) -> String {
}
}
MessageContent::ToolResponse(res) => {
if let Ok(contents) = &res.tool_result {
let text_items: Vec<String> = contents
if let Ok(result) = &res.tool_result {
let text_items: Vec<String> = result
.content
.iter()
.filter_map(|content| {
content.as_text().map(|text_str| text_str.text.clone())
@@ -517,7 +518,12 @@ mod tests {
),
Message::user().with_tool_response(
"tool_0",
Ok(vec![RawContent::text("hello, world").no_annotation()]),
Ok(rmcp::model::CallToolResult {
content: vec![RawContent::text("hello, world").no_annotation()],
structured_content: None,
is_error: Some(false),
meta: None,
}),
),
];
@@ -550,9 +556,12 @@ mod tests {
));
messages.push(Message::user().with_tool_response(
format!("tool_{}", i),
Ok(vec![
RawContent::text(format!("response{}", i)).no_annotation(),
]),
Ok(rmcp::model::CallToolResult {
content: vec![RawContent::text(format!("response{}", i)).no_annotation()],
structured_content: None,
is_error: Some(false),
meta: None,
}),
));
}
+10 -9
View File
@@ -1,9 +1,9 @@
use crate::mcp_utils::ToolResult;
use chrono::Utc;
use rmcp::model::{
AnnotateAble, CallToolRequestParam, Content, ImageContent, JsonObject, PromptMessage,
PromptMessageContent, PromptMessageRole, RawContent, RawImageContent, RawTextContent,
ResourceContents, Role, TextContent,
AnnotateAble, CallToolRequestParam, CallToolResult, Content, ImageContent, JsonObject,
PromptMessage, PromptMessageContent, PromptMessageRole, RawContent, RawImageContent,
RawTextContent, ResourceContents, Role, TextContent,
};
use serde::{Deserialize, Deserializer, Serialize};
use std::collections::HashSet;
@@ -88,7 +88,7 @@ pub struct ToolResponse {
pub id: String,
#[serde(with = "tool_result_serde")]
#[schema(value_type = Object)]
pub tool_result: ToolResult<Vec<Content>>,
pub tool_result: ToolResult<CallToolResult>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -190,7 +190,7 @@ impl fmt::Display for MessageContent {
f,
"[ToolResponse: {}]",
match &r.tool_result {
Ok(contents) => format!("{} content item(s)", contents.len()),
Ok(result) => format!("{} content item(s)", result.content.len()),
Err(e) => format!("Error: {e}"),
}
),
@@ -266,7 +266,7 @@ impl MessageContent {
})
}
pub fn tool_response<S: Into<String>>(id: S, tool_result: ToolResult<Vec<Content>>) -> Self {
pub fn tool_response<S: Into<String>>(id: S, tool_result: ToolResult<CallToolResult>) -> Self {
MessageContent::ToolResponse(ToolResponse {
id: id.into(),
tool_result,
@@ -380,8 +380,9 @@ impl MessageContent {
pub fn as_tool_response_text(&self) -> Option<String> {
if let Some(tool_response) = self.as_tool_response() {
if let Ok(contents) = &tool_response.tool_result {
let texts: Vec<String> = contents
if let Ok(result) = &tool_response.tool_result {
let texts: Vec<String> = result
.content
.iter()
.filter_map(|content| content.as_text().map(|t| t.text.to_string()))
.collect();
@@ -644,7 +645,7 @@ impl Message {
pub fn with_tool_response<S: Into<String>>(
self,
id: S,
result: ToolResult<Vec<Content>>,
result: ToolResult<CallToolResult>,
) -> Self {
self.with_content(MessageContent::tool_response(id, result))
}
+42 -5
View File
@@ -555,7 +555,15 @@ mod tests {
arguments: Some(object!({"query": "rust programming"})),
}),
),
Message::user().with_tool_response("search_1", Ok(vec![])),
Message::user().with_tool_response(
"search_1",
Ok(rmcp::model::CallToolResult {
content: vec![],
structured_content: None,
is_error: Some(false),
meta: None,
}),
),
Message::assistant().with_text("Based on the search results, here's what I found..."),
];
@@ -592,7 +600,15 @@ mod tests {
Message::user().with_text("Another user message"),
Message::assistant()
.with_text("Response")
.with_tool_response("orphan_1", Ok(vec![])), // Wrong role
.with_tool_response(
"orphan_1",
Ok(rmcp::model::CallToolResult {
content: vec![],
structured_content: None,
is_error: Some(false),
meta: None,
}),
), // Wrong role
Message::assistant().with_thinking("Let me think", "sig"),
Message::user()
.with_tool_request(
@@ -642,7 +658,15 @@ mod tests {
}),
),
Message::user(),
Message::user().with_tool_response("wrong_id", Ok(vec![])),
Message::user().with_tool_response(
"wrong_id",
Ok(rmcp::model::CallToolResult {
content: vec![],
structured_content: None,
is_error: Some(false),
meta: None,
}),
),
Message::assistant().with_tool_request(
"search_2",
Ok(CallToolRequestParam {
@@ -687,7 +711,12 @@ mod tests {
.with_tool_request("toolu_bdrk_01KgDYHs4fAodi22NqxRzmwx", Ok(CallToolRequestParam { name: "developer__shell".into(), arguments: Some(object!({"command": "wc slack.yaml"})) })),
Message::user()
.with_tool_response("toolu_bdrk_01KgDYHs4fAodi22NqxRzmwx", Ok(vec![])),
.with_tool_response("toolu_bdrk_01KgDYHs4fAodi22NqxRzmwx", Ok(rmcp::model::CallToolResult {
content: vec![],
structured_content: None,
is_error: Some(false),
meta: None,
})),
Message::assistant()
.with_text("I ran `ls -la` in the current directory and found several files. Looking at the file sizes, I can see that both `slack.yaml` and `subrecipes.yaml` are 0 bytes (the smallest files). I ran a word count on `slack.yaml` which shows: **0 lines**, **0 words**, **0 characters**"),
@@ -718,7 +747,15 @@ mod tests {
arguments: Some(object!({})),
}),
),
Message::user().with_tool_response("search_1", Ok(vec![])),
Message::user().with_tool_response(
"search_1",
Ok(rmcp::model::CallToolResult {
content: vec![],
structured_content: None,
is_error: Some(false),
meta: None,
}),
),
Message::user().with_text("Thanks!"),
];
@@ -14,7 +14,7 @@ pub struct ToolPermissionRecord {
allowed: bool,
context_hash: String, // Hash of the tool's arguments/context to differentiate similar calls
#[serde(skip_serializing_if = "Option::is_none")] // Don't serialize if None
readable_context: Option<String>, // Add this field
readable_context: Option<String>,
timestamp: i64,
expiry: Option<i64>, // Optional expiry timestamp
}
+3 -2
View File
@@ -74,9 +74,10 @@ impl ClaudeCodeProvider {
}
}
MessageContent::ToolResponse(tool_response) => {
if let Ok(tool_contents) = &tool_response.tool_result {
if let Ok(result) = &tool_response.tool_result {
// Convert tool result contents to text
let content_text = tool_contents
let content_text = result
.content
.iter()
.filter_map(|content| match &content.raw {
rmcp::model::RawContent::Text(text_content) => {
+3 -2
View File
@@ -86,8 +86,9 @@ impl CursorAgentProvider {
}
}
MessageContent::ToolResponse(tool_response) => {
if let Ok(tool_contents) = &tool_response.tool_result {
let content_text = tool_contents
if let Ok(result) = &tool_response.tool_result {
let content_text = result
.content
.iter()
.filter_map(|content| match &content.raw {
rmcp::model::RawContent::Text(text_content) => {
@@ -68,6 +68,7 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
MessageContent::ToolResponse(tool_response) => match &tool_response.tool_result {
Ok(result) => {
let text = result
.content
.iter()
.filter_map(|c| c.as_text().map(|t| t.text.clone()))
.collect::<Vec<_>>()
@@ -86,8 +86,9 @@ pub fn to_bedrock_message_content(content: &MessageContent) -> Result<bedrock::C
}
MessageContent::ToolResponse(tool_res) => {
let content = match &tool_res.tool_result {
Ok(content) => Some(
content
Ok(result) => Some(
result
.content
.iter()
// Filter out content items that have User in their audience
.filter(|c| {
@@ -318,6 +319,12 @@ pub fn from_bedrock_content_block(block: &bedrock::ContentBlock) -> Result<Messa
.iter()
.map(from_bedrock_tool_result_content_block)
.collect::<ToolResult<Vec<_>>>()
.map(|content| rmcp::model::CallToolResult {
content,
structured_content: None,
is_error: Some(false),
meta: None,
})
},
),
_ => bail!("Unsupported content block type from Bedrock"),
@@ -133,9 +133,10 @@ fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<Data
}
MessageContent::ToolResponse(response) => {
match &response.tool_result {
Ok(contents) => {
Ok(call_result) => {
// Send only contents with no audience or with Assistant in the audience
let abridged: Vec<_> = contents
let abridged: Vec<_> = call_result
.content
.iter()
.filter(|content| {
content
@@ -638,6 +639,7 @@ pub fn create_request(
mod tests {
use super::*;
use crate::conversation::message::Message;
use rmcp::model::CallToolResult;
use rmcp::object;
use serde_json::json;
@@ -727,8 +729,15 @@ mod tests {
panic!("should be tool request");
};
messages
.push(Message::user().with_tool_response(tool_id, Ok(vec![Content::text("Result")])));
messages.push(Message::user().with_tool_response(
tool_id,
Ok(CallToolResult {
content: vec![Content::text("Result")],
structured_content: None,
is_error: Some(false),
meta: None,
}),
));
let as_value =
serde_json::to_value(format_messages(&messages, &ImageFormat::OpenAi)).unwrap();
@@ -764,8 +773,15 @@ mod tests {
panic!("should be tool request");
};
messages
.push(Message::user().with_tool_response(tool_id, Ok(vec![Content::text("Result")])));
messages.push(Message::user().with_tool_response(
tool_id,
Ok(CallToolResult {
content: vec![Content::text("Result")],
structured_content: None,
is_error: Some(false),
meta: None,
}),
));
let as_value =
serde_json::to_value(format_messages(&messages, &ImageFormat::OpenAi)).unwrap();
+10 -4
View File
@@ -70,9 +70,10 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
},
MessageContent::ToolResponse(response) => {
match &response.tool_result {
Ok(contents) => {
Ok(result) => {
// Send only contents with no audience or with Assistant in the audience
let abridged: Vec<_> = contents
let abridged: Vec<_> = result
.content
.iter()
.filter(|content| {
content.audience().is_none_or(|audience| {
@@ -394,7 +395,7 @@ pub fn create_request(
mod tests {
use super::*;
use crate::conversation::message::Message;
use rmcp::model::CallToolRequestParam;
use rmcp::model::{CallToolRequestParam, CallToolResult};
use rmcp::{model::Content, object};
use serde_json::json;
@@ -429,7 +430,12 @@ mod tests {
0,
vec![MessageContent::tool_response(
id.to_string(),
Ok(tool_response),
Ok(CallToolResult {
content: tool_response,
structured_content: None,
is_error: Some(false),
meta: None,
}),
)],
)
}
+22 -6
View File
@@ -128,9 +128,10 @@ pub fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec<
},
MessageContent::ToolResponse(response) => {
match &response.tool_result {
Ok(contents) => {
Ok(result) => {
// Send only contents with no audience or with Assistant in the audience
let abridged: Vec<_> = contents
let abridged: Vec<_> = result
.content
.iter()
.filter(|content| {
content
@@ -710,6 +711,7 @@ pub fn create_request(
mod tests {
use super::*;
use crate::conversation::message::Message;
use rmcp::model::CallToolResult;
use rmcp::object;
use serde_json::json;
use tokio::pin;
@@ -868,8 +870,15 @@ mod tests {
panic!("should be tool request");
};
messages
.push(Message::user().with_tool_response(tool_id, Ok(vec![Content::text("Result")])));
messages.push(Message::user().with_tool_response(
tool_id,
Ok(CallToolResult {
content: vec![Content::text("Result")],
structured_content: None,
is_error: Some(false),
meta: None,
}),
));
let spec = format_messages(&messages, &ImageFormat::OpenAi);
@@ -904,8 +913,15 @@ mod tests {
panic!("should be tool request");
};
messages
.push(Message::user().with_tool_response(tool_id, Ok(vec![Content::text("Result")])));
messages.push(Message::user().with_tool_response(
tool_id,
Ok(CallToolResult {
content: vec![Content::text("Result")],
structured_content: None,
is_error: Some(false),
meta: None,
}),
));
let spec = format_messages(&messages, &ImageFormat::OpenAi);
@@ -37,6 +37,7 @@ pub fn format_messages(messages: &[Message]) -> Vec<Value> {
MessageContent::ToolResponse(tool_response) => {
if let Ok(result) = &tool_response.tool_result {
let text = result
.content
.iter()
.filter_map(|c| c.as_text().map(|t| t.text.clone()))
.collect::<Vec<_>>()
+2 -2
View File
@@ -215,9 +215,9 @@ impl LeadWorkerProvider {
if let Err(tool_error) = &tool_response.tool_result {
failure_indicators += 1;
tracing::debug!("Tool execution failure detected: {:?}", tool_error);
} else if let Ok(contents) = &tool_response.tool_result {
} else if let Ok(result) = &tool_response.tool_result {
// Check tool output for error indicators
if self.contains_error_indicators(contents) {
if self.contains_error_indicators(&result.content) {
failure_indicators += 1;
tracing::debug!("Tool output contains error indicators");
}
+3 -2
View File
@@ -343,8 +343,9 @@ pub fn convert_tool_messages_to_text(messages: &[Message]) -> Conversation {
has_tool_content = true;
// Convert tool response to text format
let text = match &res.tool_result {
Ok(contents) => {
let text_contents: Vec<String> = contents
Ok(result) => {
let text_contents: Vec<String> = result
.content
.iter()
.filter_map(|c| match c.deref() {
RawContent::Text(t) => Some(t.text.clone()),
@@ -118,10 +118,10 @@ mod tests {
let tool_result = result.result.await;
assert!(tool_result.is_ok());
let contents = tool_result.unwrap();
assert!(!contents.is_empty());
assert!(!contents.content.is_empty());
// Parse the returned JSON to verify task creation
if let Some(text_content) = contents.first().and_then(|c| c.as_text()) {
if let Some(text_content) = contents.content.first().and_then(|c| c.as_text()) {
let task_payload: serde_json::Value = serde_json::from_str(&text_content.text).unwrap();
assert!(task_payload.get("task_ids").is_some());
let task_ids = task_payload.get("task_ids").unwrap().as_array().unwrap();
+3 -3
View File
@@ -6,7 +6,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use std::{env, fs};
use rmcp::model::{CallToolRequestParam, Content, Tool};
use rmcp::model::{CallToolRequestParam, CallToolResult, Tool};
use rmcp::object;
use tokio_util::sync::CancellationToken;
@@ -108,7 +108,7 @@ fn build_and_get_binary_path() -> PathBuf {
}
})
.next()
.expect("failed to parase binary path")
.expect("failed to parse binary path")
}
static REPLAY_BINARY_PATH: Lazy<PathBuf> = Lazy::new(build_and_get_binary_path);
@@ -284,7 +284,7 @@ async fn test_replayed_session(
serde_json::to_writer_pretty(File::create(results_path)?, &results)?
}
TestMode::Playback => assert_eq!(
serde_json::from_reader::<_, Vec<Vec<Content>>>(File::open(results_path)?)?,
serde_json::from_reader::<_, Vec<CallToolResult>>(File::open(results_path)?)?,
results
),
};
File diff suppressed because one or more lines are too long
@@ -1,111 +1,126 @@
[
[
{
"type": "resource",
"resource": {
"uri": "file:///tmp/goose_test/goose.txt",
"mimeType": "text",
"text": "# goose\n"
{
"content": [
{
"type": "resource",
"resource": {
"uri": "file:///tmp/goose_test/goose.txt",
"mimeType": "text",
"text": "# goose\n"
},
"annotations": {
"audience": [
"assistant"
]
}
},
"annotations": {
"audience": [
"assistant"
]
{
"type": "text",
"text": "### /tmp/goose_test/goose.txt\n```\n1: # goose\n```\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.0
}
}
},
{
"type": "text",
"text": "### /tmp/goose_test/goose.txt\n```\n1: # goose\n```\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.0
],
"isError": false
},
{
"content": [
{
"type": "text",
"text": "The file /tmp/goose_test/goose.txt has been edited, and the section now reads:\n```\n# goose (modified by test)\n```\n\nReview the changes above for errors. Undo and edit the file again if necessary!\n",
"annotations": {
"audience": [
"assistant"
]
}
},
{
"type": "text",
"text": "```\n# goose (modified by test)\n```\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.2
}
}
}
],
[
{
"type": "text",
"text": "The file /tmp/goose_test/goose.txt has been edited, and the section now reads:\n```\n# goose (modified by test)\n```\n\nReview the changes above for errors. Undo and edit the file again if necessary!\n",
"annotations": {
"audience": [
"assistant"
]
],
"isError": false
},
{
"content": [
{
"type": "text",
"text": "# goose (modified by test)\n",
"annotations": {
"audience": [
"assistant"
]
}
},
{
"type": "text",
"text": "# goose (modified by test)\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.0
}
}
},
{
"type": "text",
"text": "```\n# goose (modified by test)\n```\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.2
],
"isError": false
},
{
"content": [
{
"type": "text",
"text": "The file /tmp/goose_test/goose.txt has been edited, and the section now reads:\n```\n# goose\n```\n\nReview the changes above for errors. Undo and edit the file again if necessary!\n",
"annotations": {
"audience": [
"assistant"
]
}
},
{
"type": "text",
"text": "```\n# goose\n```\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.2
}
}
}
],
[
{
"type": "text",
"text": "# goose (modified by test)\n",
"annotations": {
"audience": [
"assistant"
]
],
"isError": false
},
{
"content": [
{
"type": "text",
"text": "Available windows:\nMenubar",
"annotations": {
"audience": [
"assistant"
]
}
},
{
"type": "text",
"text": "Available windows:\nMenubar",
"annotations": {
"audience": [
"user"
],
"priority": 0.0
}
}
},
{
"type": "text",
"text": "# goose (modified by test)\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.0
}
}
],
[
{
"type": "text",
"text": "The file /tmp/goose_test/goose.txt has been edited, and the section now reads:\n```\n# goose\n```\n\nReview the changes above for errors. Undo and edit the file again if necessary!\n",
"annotations": {
"audience": [
"assistant"
]
}
},
{
"type": "text",
"text": "```\n# goose\n```\n",
"annotations": {
"audience": [
"user"
],
"priority": 0.2
}
}
],
[
{
"type": "text",
"text": "Available windows:\n\nItem-0\nbb3cc23c-6950-4e96-8b40-850e09f46934\nItem-0\nItem-0\nItem-0\nItem-0\nItem-0\nItem-0\nItem-0\nItem-0\nItem-0\nBattery\nWiFi\nItem-0\nBentoBox\nSiri\nClock\nMenubar\nDock\njust record-mcp-tests",
"annotations": {
"audience": [
"assistant"
]
}
},
{
"type": "text",
"text": "Available windows:\n\nItem-0\nbb3cc23c-6950-4e96-8b40-850e09f46934\nItem-0\nItem-0\nItem-0\nItem-0\nItem-0\nItem-0\nItem-0\nItem-0\nItem-0\nBattery\nWiFi\nItem-0\nBentoBox\nSiri\nClock\nMenubar\nDock\njust record-mcp-tests",
"annotations": {
"audience": [
"user"
],
"priority": 0.0
}
}
]
],
"isError": false
}
]
@@ -1,6 +1,12 @@
STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{"elicitation": {}, "sampling":{}},"clientInfo":{"name":"goose","version":"0.0.0"}}}
STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{"sampling":{},"elicitation":{}},"clientInfo":{"name":"goose","version":"0.0.0"}}}
STDERR: time=2025-12-11T17:58:47.636-05:00 level=INFO msg="starting server" version=0.24.1 host="" dynamicToolsets=false readOnly=false lockdownEnabled=false
STDERR: GitHub MCP Server running on stdio
STDOUT: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26","capabilities":{"logging":{},"prompts":{},"resources":{"subscribe":true,"listChanged":true},"tools":{"listChanged":true}},"serverInfo":{"name":"github-mcp-server","version":"version"}}}
STDERR: time=2025-12-11T17:58:47.640-05:00 level=INFO msg="server run start"
STDERR: time=2025-12-11T17:58:47.640-05:00 level=INFO msg="server connecting"
STDERR: time=2025-12-11T17:58:47.640-05:00 level=INFO msg="server session connected" session_id=""
STDOUT: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"completions":{},"logging":{},"prompts":{"listChanged":true},"resources":{"listChanged":true},"tools":{"listChanged":true}},"instructions":"The GitHub MCP Server provides tools to interact with GitHub platform.\n\nTool selection guidance:\n\t1. Use 'list_*' tools for broad, simple retrieval and pagination of all items of a type (e.g., all issues, all PRs, all branches) with basic filtering.\n\t2. Use 'search_*' tools for targeted queries with specific criteria, keywords, or complex filters (e.g., issues with certain text, PRs by author, code containing functions).\n\nContext management:\n\t1. Use pagination whenever possible with batches of 5-10 items.\n\t2. Use minimal_output parameter set to true if the full information is not needed to accomplish a task.\n\nTool usage guidance:\n\t1. For 'search_*' tools: Use separate 'sort' and 'order' parameters if available for sorting results - do not include 'sort:' syntax in query strings. Query strings should contain only search criteria (e.g., 'org:google language:python'), not sorting instructions. Always call 'get_me' first to understand current user permissions and context. ## Issues\n\nCheck 'list_issue_types' first for organizations to use proper issue types. Use 'search_issues' before creating new issues to avoid duplicates. Always set 'state_reason' when closing issues. ## Pull Requests\n\nPR review workflow: Always use 'pull_request_review_write' with method 'create' to create a pending review, then 'add_comment_to_pending_review' to add comments, and finally 'pull_request_review_write' with method 'submit_pending' to submit the review for complex reviews with line-specific comments.\n\nBefore creating a pull request, search for pull request templates in the repository. Template files are called pull_request_template.md or they're located in '.github/PULL_REQUEST_TEMPLATE' directory. Use the template content to structure the PR description and then call create_pull_request tool.","protocolVersion":"2025-03-26","serverInfo":{"name":"github-mcp-server","title":"GitHub MCP Server","version":"0.24.1"}}}
STDIN: {"jsonrpc":"2.0","method":"notifications/initialized"}
STDERR: time=2025-12-11T17:58:47.642-05:00 level=INFO msg="session initialized"
STDIN: {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"_meta":{"progressToken":0},"name":"get_file_contents","arguments":{"owner":"block","path":"README.md","repo":"goose","sha":"ab62b863c1666232a67048b6c4e10007a2a5b83c"}}}
STDOUT: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"successfully downloaded text file"},{"type":"resource","resource":{"uri":"repo://block/goose/sha/ab62b863c1666232a67048b6c4e10007a2a5b83c/contents/README.md","mimeType":"text/plain; charset=utf-8","text":"\u003cdiv align=\"center\"\u003e\n\n# goose\n\n_a local, extensible, open source AI agent that automates engineering tasks_\n\n\u003cp align=\"center\"\u003e\n \u003ca href=\"https://opensource.org/licenses/Apache-2.0\"\u003e\n \u003cimg src=\"https://img.shields.io/badge/License-Apache_2.0-blue.svg\"\u003e\n \u003c/a\u003e\n \u003ca href=\"https://discord.gg/7GaTvbDwga\"\u003e\n \u003cimg src=\"https://img.shields.io/discord/1287729918100246654?logo=discord\u0026logoColor=white\u0026label=Join+Us\u0026color=blueviolet\" alt=\"Discord\"\u003e\n \u003c/a\u003e\n \u003ca href=\"https://github.com/block/goose/actions/workflows/ci.yml\"\u003e\n \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/block/goose/ci.yml?branch=main\" alt=\"CI\"\u003e\n \u003c/a\u003e\n\u003c/p\u003e\n\u003c/div\u003e\n\ngoose is your on-machine AI agent, capable of automating complex development tasks from start to finish. More than just code suggestions, goose can build entire projects from scratch, write and execute code, debug failures, orchestrate workflows, and interact with external APIs - _autonomously_.\n\nWhether you're prototyping an idea, refining existing code, or managing intricate engineering pipelines, goose adapts to your workflow and executes tasks with precision.\n\nDesigned for maximum flexibility, goose works with any LLM and supports multi-model configuration to optimize performance and cost, seamlessly integrates with MCP servers, and is available as both a desktop app as well as CLI - making it the ultimate AI assistant for developers who want to move faster and focus on innovation.\n\n# Quick Links\n- [Quickstart](https://block.github.io/goose/docs/quickstart)\n- [Installation](https://block.github.io/goose/docs/getting-started/installation)\n- [Tutorials](https://block.github.io/goose/docs/category/tutorials)\n- [Documentation](https://block.github.io/goose/docs/category/getting-started)\n\n\n# Goose Around with Us\n- [Discord](https://discord.gg/block-opensource)\n- [YouTube](https://www.youtube.com/@blockopensource)\n- [LinkedIn](https://www.linkedin.com/company/block-opensource)\n- [Twitter/X](https://x.com/blockopensource)\n- [Bluesky](https://bsky.app/profile/opensource.block.xyz)\n- [Nostr](https://njump.me/opensource@block.xyz)\n"}}]}}
STDOUT: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"successfully downloaded text file (SHA: de9bdde7f260549bf3a083651842f30ab29cf4e9)"},{"type":"resource","resource":{"uri":"repo://block/goose/sha/ab62b863c1666232a67048b6c4e10007a2a5b83c/contents/README.md","mimeType":"text/plain; charset=utf-8","text":"\u003cdiv align=\"center\"\u003e\n\n# goose\n\n_a local, extensible, open source AI agent that automates engineering tasks_\n\n\u003cp align=\"center\"\u003e\n \u003ca href=\"https://opensource.org/licenses/Apache-2.0\"\u003e\n \u003cimg src=\"https://img.shields.io/badge/License-Apache_2.0-blue.svg\"\u003e\n \u003c/a\u003e\n \u003ca href=\"https://discord.gg/7GaTvbDwga\"\u003e\n \u003cimg src=\"https://img.shields.io/discord/1287729918100246654?logo=discord\u0026logoColor=white\u0026label=Join+Us\u0026color=blueviolet\" alt=\"Discord\"\u003e\n \u003c/a\u003e\n \u003ca href=\"https://github.com/block/goose/actions/workflows/ci.yml\"\u003e\n \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/block/goose/ci.yml?branch=main\" alt=\"CI\"\u003e\n \u003c/a\u003e\n\u003c/p\u003e\n\u003c/div\u003e\n\ngoose is your on-machine AI agent, capable of automating complex development tasks from start to finish. More than just code suggestions, goose can build entire projects from scratch, write and execute code, debug failures, orchestrate workflows, and interact with external APIs - _autonomously_.\n\nWhether you're prototyping an idea, refining existing code, or managing intricate engineering pipelines, goose adapts to your workflow and executes tasks with precision.\n\nDesigned for maximum flexibility, goose works with any LLM and supports multi-model configuration to optimize performance and cost, seamlessly integrates with MCP servers, and is available as both a desktop app as well as CLI - making it the ultimate AI assistant for developers who want to move faster and focus on innovation.\n\n[![Watch the video](https://github.com/user-attachments/assets/ddc71240-3928-41b5-8210-626dfb28af7a)](https://youtu.be/D-DpDunrbpo)\n\n# Quick Links\n- [Quickstart](https://block.github.io/goose/docs/quickstart)\n- [Installation](https://block.github.io/goose/docs/getting-started/installation)\n- [Tutorials](https://block.github.io/goose/docs/category/tutorials)\n- [Documentation](https://block.github.io/goose/docs/category/getting-started)\n\n\n# a little goose humor 🦢\n\n\u003e Why did the developer choose goose as their AI agent?\n\u003e \n\u003e Because it always helps them \"migrate\" their code to production! 🚀\n\n# goose around with us\n- [Discord](https://discord.gg/block-opensource)\n- [YouTube](https://www.youtube.com/@goose-oss)\n- [LinkedIn](https://www.linkedin.com/company/goose-oss)\n- [Twitter/X](https://x.com/goose_oss)\n- [Bluesky](https://bsky.app/profile/opensource.block.xyz)\n- [Nostr](https://njump.me/opensource@block.xyz)\n"}}]}}
STDERR: time=2025-12-11T17:58:48.133-05:00 level=INFO msg="server session disconnected" session_id=""
@@ -1,16 +1,18 @@
[
[
{
"type": "text",
"text": "successfully downloaded text file"
},
{
"type": "resource",
"resource": {
"uri": "repo://block/goose/sha/ab62b863c1666232a67048b6c4e10007a2a5b83c/contents/README.md",
"mimeType": "text/plain; charset=utf-8",
"text": "<div align=\"center\">\n\n# goose\n\n_a local, extensible, open source AI agent that automates engineering tasks_\n\n<p align=\"center\">\n <a href=\"https://opensource.org/licenses/Apache-2.0\">\n <img src=\"https://img.shields.io/badge/License-Apache_2.0-blue.svg\">\n </a>\n <a href=\"https://discord.gg/7GaTvbDwga\">\n <img src=\"https://img.shields.io/discord/1287729918100246654?logo=discord&logoColor=white&label=Join+Us&color=blueviolet\" alt=\"Discord\">\n </a>\n <a href=\"https://github.com/block/goose/actions/workflows/ci.yml\">\n <img src=\"https://img.shields.io/github/actions/workflow/status/block/goose/ci.yml?branch=main\" alt=\"CI\">\n </a>\n</p>\n</div>\n\ngoose is your on-machine AI agent, capable of automating complex development tasks from start to finish. More than just code suggestions, goose can build entire projects from scratch, write and execute code, debug failures, orchestrate workflows, and interact with external APIs - _autonomously_.\n\nWhether you're prototyping an idea, refining existing code, or managing intricate engineering pipelines, goose adapts to your workflow and executes tasks with precision.\n\nDesigned for maximum flexibility, goose works with any LLM and supports multi-model configuration to optimize performance and cost, seamlessly integrates with MCP servers, and is available as both a desktop app as well as CLI - making it the ultimate AI assistant for developers who want to move faster and focus on innovation.\n\n# Quick Links\n- [Quickstart](https://block.github.io/goose/docs/quickstart)\n- [Installation](https://block.github.io/goose/docs/getting-started/installation)\n- [Tutorials](https://block.github.io/goose/docs/category/tutorials)\n- [Documentation](https://block.github.io/goose/docs/category/getting-started)\n\n\n# Goose Around with Us\n- [Discord](https://discord.gg/block-opensource)\n- [YouTube](https://www.youtube.com/@blockopensource)\n- [LinkedIn](https://www.linkedin.com/company/block-opensource)\n- [Twitter/X](https://x.com/blockopensource)\n- [Bluesky](https://bsky.app/profile/opensource.block.xyz)\n- [Nostr](https://njump.me/opensource@block.xyz)\n"
{
"content": [
{
"type": "text",
"text": "successfully downloaded text file (SHA: de9bdde7f260549bf3a083651842f30ab29cf4e9)"
},
{
"type": "resource",
"resource": {
"uri": "repo://block/goose/sha/ab62b863c1666232a67048b6c4e10007a2a5b83c/contents/README.md",
"mimeType": "text/plain; charset=utf-8",
"text": "<div align=\"center\">\n\n# goose\n\n_a local, extensible, open source AI agent that automates engineering tasks_\n\n<p align=\"center\">\n <a href=\"https://opensource.org/licenses/Apache-2.0\">\n <img src=\"https://img.shields.io/badge/License-Apache_2.0-blue.svg\">\n </a>\n <a href=\"https://discord.gg/7GaTvbDwga\">\n <img src=\"https://img.shields.io/discord/1287729918100246654?logo=discord&logoColor=white&label=Join+Us&color=blueviolet\" alt=\"Discord\">\n </a>\n <a href=\"https://github.com/block/goose/actions/workflows/ci.yml\">\n <img src=\"https://img.shields.io/github/actions/workflow/status/block/goose/ci.yml?branch=main\" alt=\"CI\">\n </a>\n</p>\n</div>\n\ngoose is your on-machine AI agent, capable of automating complex development tasks from start to finish. More than just code suggestions, goose can build entire projects from scratch, write and execute code, debug failures, orchestrate workflows, and interact with external APIs - _autonomously_.\n\nWhether you're prototyping an idea, refining existing code, or managing intricate engineering pipelines, goose adapts to your workflow and executes tasks with precision.\n\nDesigned for maximum flexibility, goose works with any LLM and supports multi-model configuration to optimize performance and cost, seamlessly integrates with MCP servers, and is available as both a desktop app as well as CLI - making it the ultimate AI assistant for developers who want to move faster and focus on innovation.\n\n[![Watch the video](https://github.com/user-attachments/assets/ddc71240-3928-41b5-8210-626dfb28af7a)](https://youtu.be/D-DpDunrbpo)\n\n# Quick Links\n- [Quickstart](https://block.github.io/goose/docs/quickstart)\n- [Installation](https://block.github.io/goose/docs/getting-started/installation)\n- [Tutorials](https://block.github.io/goose/docs/category/tutorials)\n- [Documentation](https://block.github.io/goose/docs/category/getting-started)\n\n\n# a little goose humor 🦢\n\n> Why did the developer choose goose as their AI agent?\n> \n> Because it always helps them \"migrate\" their code to production! 🚀\n\n# goose around with us\n- [Discord](https://discord.gg/block-opensource)\n- [YouTube](https://www.youtube.com/@goose-oss)\n- [LinkedIn](https://www.linkedin.com/company/goose-oss)\n- [Twitter/X](https://x.com/goose_oss)\n- [Bluesky](https://bsky.app/profile/opensource.block.xyz)\n- [Nostr](https://njump.me/opensource@block.xyz)\n"
}
}
}
]
]
]
}
]
@@ -1,10 +1,10 @@
STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{"elicitation": {}, "sampling":{}},"clientInfo":{"name":"goose","version":"0.0.0"}}}
STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{"sampling":{},"elicitation":{}},"clientInfo":{"name":"goose","version":"0.0.0"}}}
STDERR: Starting default (STDIO) server...
STDERR: Starting logs update interval
STDOUT: {"result":{"protocolVersion":"2025-03-26","capabilities":{"prompts":{},"resources":{"subscribe":true},"tools":{},"logging":{},"completions":{}},"serverInfo":{"name":"example-servers/everything","title":"Everything Example Server","version":"1.0.0"},"instructions":"Testing and demonstration server for MCP protocol features.\n\n## Resources\n\nResources 1-100 follow pattern: even IDs contain text, odd IDs contain binary data. Resources are paginated at 10 items per page with cursor-based navigation.\n\n## Key dependencies\n\nProgress notifications require `_meta.progressToken` in tool calls. Resource subscriptions generate updates every 10 seconds.\n\n## Performance characteristics\n\nServer generates automatic log messages every 20 seconds (filtered by current log level), stderr notifications every 30 seconds, and resource update notifications every 10 seconds for active subscriptions. Log level changes affect message filtering in real-time.\n\n## Multi-modal testing\n\n`complex_prompt` includes both text arguments and image content for testing client multi-modal handling. `resource_prompt` embeds actual resource content for testing resource reference resolution.\n\nArgument completion is available for prompt parameters and resource IDs. Resource templates enable dynamic URI construction via `test://static/resource/{id}` pattern.\n\n## Easter egg\n\nIf asked about server instructions, respond with \"🎉 Server instructions are working! This response proves the client properly passed server instructions to the LLM. This demonstrates MCP's instructions feature in action.\"\n"},"jsonrpc":"2.0","id":0}
STDIN: {"jsonrpc":"2.0","method":"notifications/initialized"}
STDOUT: {"method":"notifications/message","params":{"level":"info","logger":"everything-server","data":"Client does not support MCP roots protocol"},"jsonrpc":"2.0"}
STDIN: {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"_meta":{"progressToken":0},"name":"echo","arguments":{"message":"Hello, world!"}}}
STDOUT: {"method":"notifications/message","params":{"level":"info","logger":"everything-server","data":"Client does not support MCP roots protocol"},"jsonrpc":"2.0"}
STDOUT: {"result":{"content":[{"type":"text","text":"Echo: Hello, world!"}]},"jsonrpc":"2.0","id":1}
STDIN: {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"_meta":{"progressToken":1},"name":"add","arguments":{"a":1,"b":2}}}
STDOUT: {"result":{"content":[{"type":"text","text":"The sum of 1 and 2 is 3."}]},"jsonrpc":"2.0","id":2}
@@ -21,5 +21,5 @@ STDIN: {"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"_meta":{"progres
STDOUT: {"method":"sampling/createMessage","params":{"messages":[{"role":"user","content":{"type":"text","text":"Resource sampleLLM context: Please provide a quote from The Great Gatsby"}}],"systemPrompt":"You are a helpful test server.","maxTokens":100,"temperature":0.7,"includeContext":"thisServer"},"jsonrpc":"2.0","id":0}
STDIN: {"jsonrpc":"2.0","id":0,"result":{"model":"mock","stopReason":"endTurn","role":"assistant","content":{"type":"text","text":"\"So we beat on, boats against the current, borne back ceaselessly into the past.\" — F. Scott Fitzgerald, The Great Gatsby (1925)"}}}
STDOUT: {"result":{"content":[{"type":"text","text":"LLM sampling result: \"So we beat on, boats against the current, borne back ceaselessly into the past.\" — F. Scott Fitzgerald, The Great Gatsby (1925)"}]},"jsonrpc":"2.0","id":5}
STDOUT: {"method":"notifications/message","params":{"level":"error","data":"Error-level message"},"jsonrpc":"2.0"}
STDERR: node:events:486
STDOUT: {"method":"notifications/message","params":{"level":"critical","data":"Critical-level message"},"jsonrpc":"2.0"}
STDERR: node:events:485
@@ -1,32 +1,47 @@
[
[
{
"type": "text",
"text": "Echo: Hello, world!"
{
"content": [
{
"type": "text",
"text": "Echo: Hello, world!"
}
]
},
{
"content": [
{
"type": "text",
"text": "The sum of 1 and 2 is 3."
}
]
},
{
"content": [
{
"type": "text",
"text": "Long running operation completed. Duration: 1 seconds, Steps: 5."
}
]
},
{
"content": [
{
"type": "text",
"text": "{\"temperature\":22.5,\"conditions\":\"Partly cloudy\",\"humidity\":65}"
}
],
"structuredContent": {
"conditions": "Partly cloudy",
"humidity": 65,
"temperature": 22.5
}
],
[
{
"type": "text",
"text": "The sum of 1 and 2 is 3."
}
],
[
{
"type": "text",
"text": "Long running operation completed. Duration: 1 seconds, Steps: 5."
}
],
[
{
"type": "text",
"text": "{\"temperature\":22.5,\"conditions\":\"Partly cloudy\",\"humidity\":65}"
}
],
[
{
"type": "text",
"text": "LLM sampling result: \"So we beat on, boats against the current, borne back ceaselessly into the past.\" — F. Scott Fitzgerald, The Great Gatsby (1925)"
}
]
},
{
"content": [
{
"type": "text",
"text": "LLM sampling result: \"So we beat on, boats against the current, borne back ceaselessly into the past.\" — F. Scott Fitzgerald, The Great Gatsby (1925)"
}
]
}
]
@@ -1,5 +1,5 @@
STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{"elicitation": {}, "sampling":{}},"clientInfo":{"name":"goose","version":"0.0.0"}}}
STDOUT: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26","capabilities":{"experimental":{},"prompts":{"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"mcp-fetch","version":"1.19.0"}}}
STDIN: {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{"sampling":{},"elicitation":{}},"clientInfo":{"name":"goose","version":"0.0.0"}}}
STDOUT: {"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2025-03-26","capabilities":{"experimental":{},"prompts":{"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"mcp-fetch","version":"1.23.3"}}}
STDIN: {"jsonrpc":"2.0","method":"notifications/initialized"}
STDIN: {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"_meta":{"progressToken":0},"name":"fetch","arguments":{"url":"https://example.com"}}}
STDOUT: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"Failed to fetch robots.txt https://example.com/robots.txt due to a connection issue"}],"isError":true}}
STDOUT: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"Contents of https://example.com/:\nThis domain is for use in documentation examples without needing permission. Avoid use in operations.\n\n[Learn more](https://iana.org/domains/example)"}],"isError":false}}
@@ -1,8 +1,11 @@
[
[
{
"type": "text",
"text": "Failed to fetch robots.txt https://example.com/robots.txt due to a connection issue"
}
]
{
"content": [
{
"type": "text",
"text": "Contents of https://example.com/:\nThis domain is for use in documentation examples without needing permission. Avoid use in operations.\n\n[Learn more](https://iana.org/domains/example)"
}
],
"isError": false
}
]
+17 -7
View File
@@ -166,8 +166,9 @@ impl ProviderTester {
let weather = Message::user().with_tool_response(
id,
Ok(vec![Content::text(
"
Ok(rmcp::model::CallToolResult {
content: vec![Content::text(
"
50°F°C
Precipitation: 0%
Humidity: 84%
@@ -175,7 +176,11 @@ impl ProviderTester {
Weather
Saturday 9:00 PM
Clear",
)]),
)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
);
let (response2, _) = self
@@ -318,10 +323,15 @@ impl ProviderTester {
);
let tool_response = Message::user().with_tool_response(
"test_id",
Ok(vec![Content::image(
image_content.data.clone(),
image_content.mime_type.clone(),
)]),
Ok(rmcp::model::CallToolResult {
content: vec![Content::image(
image_content.data.clone(),
image_content.mime_type.clone(),
)],
structured_content: None,
is_error: Some(false),
meta: None,
}),
);
let result2 = self
+152
View File
@@ -86,6 +86,90 @@
}
}
},
"/agent/call_tool": {
"post": {
"tags": [
"super::routes::agent"
],
"operationId": "call_tool",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CallToolRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Resource read successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CallToolResponse"
}
}
}
},
"401": {
"description": "Unauthorized - invalid secret key"
},
"404": {
"description": "Resource not found"
},
"424": {
"description": "Agent not initialized"
},
"500": {
"description": "Internal server error"
}
}
}
},
"/agent/read_resource": {
"post": {
"tags": [
"super::routes::agent"
],
"operationId": "read_resource",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ReadResourceRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Resource read successfully",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ReadResourceResponse"
}
}
}
},
"401": {
"description": "Unauthorized - invalid secret key"
},
"404": {
"description": "Resource not found"
},
"424": {
"description": "Agent not initialized"
},
"500": {
"description": "Internal server error"
}
}
}
},
"/agent/remove_extension": {
"post": {
"tags": [
@@ -2537,6 +2621,44 @@
}
}
},
"CallToolRequest": {
"type": "object",
"required": [
"session_id",
"name",
"arguments"
],
"properties": {
"arguments": {},
"name": {
"type": "string"
},
"session_id": {
"type": "string"
}
}
},
"CallToolResponse": {
"type": "object",
"required": [
"content",
"is_error"
],
"properties": {
"content": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Content"
}
},
"is_error": {
"type": "boolean"
},
"structured_content": {
"nullable": true
}
}
},
"ChatRequest": {
"type": "object",
"required": [
@@ -4286,6 +4408,36 @@
}
}
},
"ReadResourceRequest": {
"type": "object",
"required": [
"session_id",
"extension_name",
"uri"
],
"properties": {
"extension_name": {
"type": "string"
},
"session_id": {
"type": "string"
},
"uri": {
"type": "string"
}
}
},
"ReadResourceResponse": {
"type": "object",
"required": [
"html"
],
"properties": {
"html": {
"type": "string"
}
}
},
"Recipe": {
"type": "object",
"required": [
+19 -1
View File
@@ -2,7 +2,7 @@
import type { Client, Options as Options2, TDataShape } from './client';
import { client } from './client.gen';
import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, CheckProviderData, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateRouterToolSelectorData, UpdateRouterToolSelectorErrors, UpdateRouterToolSelectorResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen';
import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, CallToolData, CallToolErrors, CallToolResponses, CheckProviderData, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateRouterToolSelectorData, UpdateRouterToolSelectorErrors, UpdateRouterToolSelectorResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen';
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = Options2<TData, ThrowOnError> & {
/**
@@ -36,6 +36,24 @@ export const agentAddExtension = <ThrowOnError extends boolean = false>(options:
}
});
export const callTool = <ThrowOnError extends boolean = false>(options: Options<CallToolData, ThrowOnError>) => (options.client ?? client).post<CallToolResponses, CallToolErrors, ThrowOnError>({
url: '/agent/call_tool',
...options,
headers: {
'Content-Type': 'application/json',
...options.headers
}
});
export const readResource = <ThrowOnError extends boolean = false>(options: Options<ReadResourceData, ThrowOnError>) => (options.client ?? client).post<ReadResourceResponses, ReadResourceErrors, ThrowOnError>({
url: '/agent/read_resource',
...options,
headers: {
'Content-Type': 'application/json',
...options.headers
}
});
export const agentRemoveExtension = <ThrowOnError extends boolean = false>(options: Options<AgentRemoveExtensionData, ThrowOnError>) => (options.client ?? client).post<AgentRemoveExtensionResponses, AgentRemoveExtensionErrors, ThrowOnError>({
url: '/agent/remove_extension',
...options,
+92
View File
@@ -46,6 +46,18 @@ export type AuthorRequest = {
metadata?: string | null;
};
export type CallToolRequest = {
arguments: unknown;
name: string;
session_id: string;
};
export type CallToolResponse = {
content: Array<Content>;
is_error: boolean;
structured_content?: unknown;
};
export type ChatRequest = {
messages: Array<Message>;
recipe_name?: string | null;
@@ -603,6 +615,16 @@ export type RawTextContent = {
text: string;
};
export type ReadResourceRequest = {
extension_name: string;
session_id: string;
uri: string;
};
export type ReadResourceResponse = {
html: string;
};
export type Recipe = {
activities?: Array<string> | null;
author?: Author | null;
@@ -1078,6 +1100,76 @@ export type AgentAddExtensionResponses = {
export type AgentAddExtensionResponse = AgentAddExtensionResponses[keyof AgentAddExtensionResponses];
export type CallToolData = {
body: CallToolRequest;
path?: never;
query?: never;
url: '/agent/call_tool';
};
export type CallToolErrors = {
/**
* Unauthorized - invalid secret key
*/
401: unknown;
/**
* Resource not found
*/
404: unknown;
/**
* Agent not initialized
*/
424: unknown;
/**
* Internal server error
*/
500: unknown;
};
export type CallToolResponses = {
/**
* Resource read successfully
*/
200: CallToolResponse;
};
export type CallToolResponse2 = CallToolResponses[keyof CallToolResponses];
export type ReadResourceData = {
body: ReadResourceRequest;
path?: never;
query?: never;
url: '/agent/read_resource';
};
export type ReadResourceErrors = {
/**
* Unauthorized - invalid secret key
*/
401: unknown;
/**
* Resource not found
*/
404: unknown;
/**
* Agent not initialized
*/
424: unknown;
/**
* Internal server error
*/
500: unknown;
};
export type ReadResourceResponses = {
/**
* Resource read successfully
*/
200: ReadResourceResponse;
};
export type ReadResourceResponse2 = ReadResourceResponses[keyof ReadResourceResponses];
export type AgentRemoveExtensionData = {
body: RemoveExtensionRequest;
path?: never;