goose icon indicating copy to clipboard operation
goose copied to clipboard

Session corruption - ui failed to load session

Open zanesq opened this issue 7 months ago • 0 comments

Describe the bug

Session corruption with bad session jsonl file still happening occasionally. Not sure how to reproduce as it seems intermittent. Possibly file writing related.

Related to: https://github.com/block/goose/issues/1946

Image

Error message from UI:

TypeError: Cannot read properties of undefined (reading 'sessionId')

Attached sample session file with problem note last line has malfomed data ole":

{"working_dir":"/Users/zane/Development/mcp_goose_configwatcher","description":"File watcher config setup","message_count":14,"total_tokens":12710,"input_tokens":11685,"output_tokens":1025,"accumulated_total_tokens":75280,"accumulated_input_tokens":72872,"accumulated_output_tokens":2408}
{"role":"user","created":1747178328,"content":[{"type":"text","text":"I have started a basic mcp template and I want to make it so it will alert me when the goose config file changes at /Users/zane/.config/goose/config.yaml so can we have a process that watches this file and sends back the changes to the llm when they happen?"}]}
{"role":"assistant","created":1747178334,"content":[{"type":"text","text":"I'll help you create a file watcher for the Goose config file. Let's break this down into steps:\n\n1. First, let's check the current directory structure to see what we're working with."},{"type":"toolRequest","id":"toolu_bdrk_01H6Vzjip2dzWDWJMQkiwevZ","toolCall":{"status":"success","value":{"name":"developer__shell","arguments":{"command":"ls -la"}}}}]}
{"role":"user","created":1747178334,"content":[{"type":"toolResponse","id":"toolu_bdrk_01H6Vzjip2dzWDWJMQkiwevZ","toolResult":{"status":"success","value":[{"type":"text","text":"total 32\ndrwxr-xr-x   8 zane  staff   256 May 13 16:13 .\ndrwxr-xr-x  24 zane  staff   768 May 13 16:13 ..\ndrwxr-xr-x   3 zane  staff    96 May 13 16:13 .buildkite\ndrwxr-xr-x@  7 zane  staff   224 May 13 16:13 .idea\n-rw-r--r--   1 zane  staff  1577 May 13 16:13 README.md\n-rw-r--r--   1 zane  staff  1226 May 13 16:13 index.ts\n-rw-r--r--   1 zane  staff   613 May 13 16:13 package.json\n-rw-r--r--   1 zane  staff   367 May 13 16:13 tsconfig.json\n","annotations":{"audience":["assistant"]}},{"type":"text","text":"total 32\ndrwxr-xr-x   8 zane  staff   256 May 13 16:13 .\ndrwxr-xr-x  24 zane  staff   768 May 13 16:13 ..\ndrwxr-xr-x   3 zane  staff    96 May 13 16:13 .buildkite\ndrwxr-xr-x@  7 zane  staff   224 May 13 16:13 .idea\n-rw-r--r--   1 zane  staff  1577 May 13 16:13 README.md\n-rw-r--r--   1 zane  staff  1226 May 13 16:13 index.ts\n-rw-r--r--   1 zane  staff   613 May 13 16:13 package.json\n-rw-r--r--   1 zane  staff   367 May 13 16:13 tsconfig.json\n","annotations":{"audience":["user"],"priority":0.0}}]}}]}
{"role":"assistant","created":1747178338,"content":[{"type":"text","text":"Let's create a file watcher using Node.js since this is a TypeScript project. We'll use the `chokidar` package for file watching. First, let's install the necessary dependencies:"},{"type":"toolRequest","id":"toolu_bdrk_01MmE6d9zNbZZdKzURaC5tk3","toolCall":{"status":"success","value":{"name":"developer__shell","arguments":{"command":"npm install chokidar @types/node"}}}}]}
{"role":"user","created":1747178338,"content":[{"type":"toolResponse","id":"toolu_bdrk_01MmE6d9zNbZZdKzURaC5tk3","toolResult":{"status":"success","value":[{"type":"text","text":"npm warn deprecated [email protected]: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.\nnpm warn deprecated [email protected]: Glob versions prior to v9 are no longer supported\n\nadded 103 packages, and audited 104 packages in 5s\n\n16 packages are looking for funding\n  run `npm fund` for details\n\nfound 0 vulnerabilities\n","annotations":{"audience":["assistant"]}},{"type":"text","text":"npm warn deprecated [email protected]: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.\nnpm warn deprecated [email protected]: Glob versions prior to v9 are no longer supported\n\nadded 103 packages, and audited 104 packages in 5s\n\n16 packages are looking for funding\n  run `npm fund` for details\n\nfound 0 vulnerabilities\n","annotations":{"audience":["user"],"priority":0.0}}]}}]}
{"role":"assistant","created":1747178349,"content":[{"type":"text","text":"Now, let's modify the `index.ts` file to implement the file watcher. First, let's see what's currently in the file:"},{"type":"toolRequest","id":"toolu_bdrk_01VhGgY7xJVLTRqiv2pLFedU","toolCall":{"status":"success","value":{"name":"developer__text_editor","arguments":{"command":"view","path":"/Users/zane/Development/mcp_goose_configwatcher/index.ts"}}}}]}
{"role":"user","created":1747178349,"content":[{"type":"toolResponse","id":"toolu_bdrk_01VhGgY7xJVLTRqiv2pLFedU","toolResult":{"status":"success","value":[{"type":"resource","resource":{"uri":"file:///Users/zane/Development/mcp_goose_configwatcher/index.ts","mime_type":"text","text":"import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\n\n// Update the instructions for your MCP server\nconst instructions =\n  \"This is the instruction / prompt for your MCP server. Include instructions on when to use this MCP server and what it can do.\";\n\n// Create an MCP server\nconst server = new McpServer(\n  {\n    name: \"mcp_ts\",\n    version: \"0.0.1\",\n  },\n  {\n    capabilities: {\n      tools: {},\n      resources: {},\n    },\n    instructions: instructions,\n  }\n);\n\n// Tool for addition\nserver.tool(\n    \"add\", // name\n    \"Adds two numbers\", // description\n    { a: z.number(), b: z.number() }, // params schema\n    { readOnlyHint: true }, // tool annotations\n    async ({ a, b }) => ({\n        content: [{ type: \"text\", text: String(a + b) }],\n    })\n);\n\n\n// Static resource\n// Static resource\nserver.resource(\n  \"config\",\n  \"config://app\",\n  async (uri) => ({\n    contents: [{\n      uri: uri.href,\n      text: \"App configuration here\"\n    }]\n  })\n);\n\n// Start receiving messages on stdin and sending messages on stdout\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n"},"annotations":{"audience":["assistant"]}},{"type":"text","text":"### /Users/zane/Development/mcp_goose_configwatcher/index.ts\n```typescript\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\n\n// Update the instructions for your MCP server\nconst instructions =\n  \"This is the instruction / prompt for your MCP server. Include instructions on when to use this MCP server and what it can do.\";\n\n// Create an MCP server\nconst server = new McpServer(\n  {\n    name: \"mcp_ts\",\n    version: \"0.0.1\",\n  },\n  {\n    capabilities: {\n      tools: {},\n      resources: {},\n    },\n    instructions: instructions,\n  }\n);\n\n// Tool for addition\nserver.tool(\n    \"add\", // name\n    \"Adds two numbers\", // description\n    { a: z.number(), b: z.number() }, // params schema\n    { readOnlyHint: true }, // tool annotations\n    async ({ a, b }) => ({\n        content: [{ type: \"text\", text: String(a + b) }],\n    })\n);\n\n\n// Static resource\n// Static resource\nserver.resource(\n  \"config\",\n  \"config://app\",\n  async (uri) => ({\n    contents: [{\n      uri: uri.href,\n      text: \"App configuration here\"\n    }]\n  })\n);\n\n// Start receiving messages on stdin and sending messages on stdout\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n\n```\n","annotations":{"audience":["user"],"priority":0.0}}]}}]}
{"role":"assistant","created":1747178364,"content":[{"type":"text","text":"Let's modify this file to implement the config file watcher. We'll:\n1. Add chokidar for file watching\n2. Create a tool that returns the current config content\n3. Set up a resource that will be updated when the config changes\n\nHere's the updated version:"},{"type":"toolRequest","id":"toolu_bdrk_01RJvUzs23XAty6XUuVyG2ti","toolCall":{"status":"success","value":{"name":"developer__text_editor","arguments":{"command":"write","file_text":"import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimpimport { z } from \"zod\";\nimport * as chokidar from 'chokidar';\nimport * as fs from 'fs/promises';\n\nconst CONFIG_PATH = '/Users/zane/.config/goose/config.yaml';\n\n// Update the instructions for your MCP server\nconst instructions =\n  \"This MCP server watches the Goose config file for changes and provides tools to access its content.\";\n\n// Create an MCP server\nconst server = new McpServer(\n  {\n    name: \"goose_config_watcher\",\n    version: \"0.0.1\",\n  },\n  {\n    capabilities: {\n      tools: {},\n      resources: {},\n    },\n    instructions: instructions,\n  }\n);\n\n// Keep track of the current config content\nlet currentConfig = '';\n\n// Function to read the config file\nasync function readConfig(): Promise<string> {\n  try {\n    return await fs.readFile(CONFIG_PATH, 'utf8');\n  } catch (error) {\n    console.error('Error reading config file:', error);\n    return '';\n  }\n}\n\n// Initialize the watcher\nconst watcher = chokidar.watch(CONFIG_PATH, {\n  persistent: true,\n  awaitWriteFinish: {\n    stabilityThreshold: 300,\n    pollInterval: 100\n  }\n});\n\n// Update config content when file changes\nwatcher.on('change', async (path) => {\n  console.log('Config file changed');\n  currentConfig = await readConfig();\n  \n  // Notify the LLM through the resource update\n  server.updateResource('config', 'config://goose', {\n    contents: [{\n      uri: 'config://goose',\n      text: currentConfig\n    }]\n  });\n});\n\n// Tool to get current config content\nserver.tool(\n  \"getConfig\",\n  \"Returns the current content of the Goose config file\",\n  {}, // no parameters needed\n  { readOnlyHint: true },\n  async () => ({\n    content: [{ type: \"text\", text: currentConfig }],\n  })\n);\n\n// Resource that provides the config content\nserver.resource(\n  \"config\",\n  \"config://goose\",\n  async (uri) => ({\n    contents: [{\n      uri: uri.href,\n      text: currentConfig\n    }]\n  })\n);\n\n// Initialize the config content\nreadConfig().then(content => {\n  currentConfig = content;\n  console.log('Initial config loaded');\n});\n\n// Start receiving messages on stdin and sending messages on stdout\nconst transport = new StdioServerTransport();\nawait server.connect(transport);","path":"/Users/zane/Development/mcp_goose_configwatcher/index.ts"}}}}]}
{"role":"user","created":1747178364,"content":[{"type":"toolResponse","id":"toolu_bdrk_01RJvUzs23XAty6XUuVyG2ti","toolResult":{"status":"success","value":[{"type":"text","text":"Successfully wrote to /Users/zane/Development/mcp_goose_configwatcher/index.ts","annotations":{"audience":["assistant"]}},{"type":"text","text":"### /Users/zane/Development/mcp_goose_configwatcher/index.ts\n```typescript\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport * as chokidar from 'chokidar';\nimport * as fs from 'fs/promises';\n\nconst CONFIG_PATH = '/Users/zane/.config/goose/config.yaml';\n\n// Update the instructions for your MCP server\nconst instructions =\n  \"This MCP server watches the Goose config file for changes and provides tools to access its content.\";\n\n// Create an MCP server\nconst server = new McpServer(\n  {\n    name: \"goose_config_watcher\",\n    version: \"0.0.1\",\n  },\n  {\n    capabilities: {\n      tools: {},\n      resources: {},\n    },\n    instructions: instructions,\n  }\n);\n\n// Keep track of the current config content\nlet currentConfig = '';\n\n// Function to read the config file\nasync function readConfig(): Promise<string> {\n  try {\n    return await fs.readFile(CONFIG_PATH, 'utf8');\n  } catch (error) {\n    console.error('Error reading config file:', error);\n    return '';\n  }\n}\n\n// Initialize the watcher\nconst watcher = chokidar.watch(CONFIG_PATH, {\n  persistent: true,\n  awaitWriteFinish: {\n    stabilityThreshold: 300,\n    pollInterval: 100\n  }\n});\n\n// Update config content when file changes\nwatcher.on('change', async (path) => {\n  console.log('Config file changed');\n  currentConfig = await readConfig();\n  \n  // Notify the LLM through the resource update\n  server.updateResource('config', 'config://goose', {\n    contents: [{\n      uri: 'config://goose',\n      text: currentConfig\n    }]\n  });\n});\n\n// Tool to get current config content\nserver.tool(\n  \"getConfig\",\n  \"Returns the current content of the Goose config file\",\n  {}, // no parameters needed\n  { readOnlyHint: true },\n  async () => ({\n    content: [{ type: \"text\", text: currentConfig }],\n  })\n);\n\n// Resource that provides the config content\nserver.resource(\n  \"config\",\n  \"config://goose\",\n  async (uri) => ({\n    contents: [{\n      uri: uri.href,\n      text: currentConfig\n    }]\n  })\n);\n\n// Initialize the config content\nreadConfig().then(content => {\n  currentConfig = content;\n  console.log('Initial config loaded');\n});\n\n// Start receiving messages on stdin and sending messages on stdout\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n```\n","annotations":{"audience":["user"],"priority":0.2}}]}}]}
{"role":"assistant","created":1747178367,"content":[{"type":"text","text":"Now let's update the `package.json` to ensure we have the correct type module setting and scripts:"},{"type":"toolRequest","id":"toolu_bdrk_01GCRpVsfjxm75w1EK2vxz5q","toolCall":{"status":"success","value":{"name":"developer__text_editor","arguments":{"command":"view","path":"/Users/zane/Development/mcp_goose_configwatcher/package.json"}}}}]}
{"role":"user","created":1747178367,"content":[{"type":"toolResponse","id":"toolu_bdrk_01GCRpVsfjxm75w1EK2vxz5q","toolResult":{"status":"success","value":[{"type":"resource","resource":{"uri":"file:///Users/zane/Development/mcp_goose_configwatcher/package.json","mime_type":"text","text":"{\n  \"name\": \"@block/mcp_goose_configwatcher\",\n  \"version\": \"0.0.1\",\n  \"description\": \"MCP Server for mcp_goose_configwatcher\",\n  \"license\": \"MIT\",\n  \"author\": \"Block, Inc\",\n  \"homepage\": \"\",\n  \"bugs\": \"\",\n  \"type\": \"module\",\n  \"bin\": {\n    \"mcp_goose_configwatcher\": \"dist/index.js\"\n  },\n  \"files\": [\n    \"dist\"\n  ],\n  \"scripts\": {\n    \"build\": \"tsc && shx chmod +x dist/*.js\",\n    \"prepare\": \"npm run build\",\n    \"watch\": \"tsc --watch\"\n  },\n  \"dependencies\": {\n    \"@modelcontextprotocol/sdk\": \"^1.11.0\",\n    \"chokidar\": \"^4.0.3\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^22.15.17\",\n    \"shx\": \"^0.3.4\",\n    \"typescript\": \"^5.6.2\"\n  }\n}\n"},"annotations":{"audience":["assistant"]}},{"type":"text","text":"### /Users/zane/Development/mcp_goose_configwatcher/package.json\n```json\n{\n  \"name\": \"@block/mcp_goose_configwatcher\",\n  \"version\": \"0.0.1\",\n  \"description\": \"MCP Server for mcp_goose_configwatcher\",\n  \"license\": \"MIT\",\n  \"author\": \"Block, Inc\",\n  \"homepage\": \"\",\n  \"bugs\": \"\",\n  \"type\": \"module\",\n  \"bin\": {\n    \"mcp_goose_configwatcher\": \"dist/index.js\"\n  },\n  \"files\": [\n    \"dist\"\n  ],\n  \"scripts\": {\n    \"build\": \"tsc && shx chmod +x dist/*.js\",\n    \"prepare\": \"npm run build\",\n    \"watch\": \"tsc --watch\"\n  },\n  \"dependencies\": {\n    \"@modelcontextprotocol/sdk\": \"^1.11.0\",\n    \"chokidar\": \"^4.0.3\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^22.15.17\",\n    \"shx\": \"^0.3.4\",\n    \"typescript\": \"^5.6.2\"\n  }\n}\n\n```\n","annotations":{"audience":["user"],"priority":0.0}}]}}]}
{"role":"assistant","created":1747178370,"content":[{"type":"text","text":"The package.json looks good already. Now let's build and run the server:"},{"type":"toolRequest","id":"toolu_bdrk_015iHbw69b7bn4LmsgFayo6F","toolCall":{"status":"success","value":{"name":"developer__shell","arguments":{"command":"npm run build"}}}}]}
{"role":"user","created":1747178370,"content":[{"type":"toolResponse","id":"toolu_bdrk_015iHbw69b7bn4LmsgFayo6F","toolResult":{"status":"success","value":[{"type":"text","text":"\n> @block/[email protected] buil> t> tsc && shx chmod +x dist/*.js\n\nindex.ts(56,10): error TS2339: Property 'updateResource' does not exist on type 'McpServer'.\n","annotations":{"audience":["assistant"]}},{"type":"text","text":"\n> @block/[email protected] build\n> tsc && shx chmod +x dist/*.js\n\nindex.ts(56,10): error TS2339: Property 'updateResource' does not exist on type 'McpServer'.\n","annotations":{"audience":["user"],"priority":0.0}}]}}]}
{"role":"assistant","created":1747178389,"content":[{"type":"text","text":"Ah, I apologize for the error. It seems the `updateResource` method isn't available in the current MCP SDK. Let's modify our approach slightly to use a different method for handling updates. Here's the corrected version:"},{"type":"toolRequest","id":"toolu_bdrk_01NX8eskWWV81bAcRLPcPg1d","toolCall":{"status":"success","value":{"name":"developer__text_editor","arguments":{"command":"write","file_text":"import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport * as chokidar from 'chokidar';\nimport * as fs from 'fs/promises';\n\nconst CONFIG_PATH = '/Users/zane/.config/goose/config.yaml';\n\n// Update the instructions for your MCP server\nconst instructions =\n  \"This MCP server watches the Goose config file for changes and provides tools to access its content.\";\n\n// Create an MCP server\nconst server = new McpServer(\n  {\n    name: \"goose_config_watcher\",\n    version: \"0.0.1\",\n  },\n  {\n    capabilities: {\n      tools: {},\n      resources: {},\n    },\n    instructions: instructions,\n  }\n);\n\n// Keep track of the current config content\nlet currentConfig = '';\nlet lastModified = new Date(0);\n\n// Function to read the config file\nasync function readConfig(): Promise<string> {\n  try {\n    return await fs.readFile(CONFIG_PATH, 'utf8');\n  } catch (error) {\n    console.error('Error reading config file:', error);\n    return '';\n  }\n}\n\n// Initialize the watcher\nconst watcher = chokidar.watch(CONFIG_PATH, {\n  persistent: true,\n  awaitWriteFinish: {\n    stabilityThreshold: 300,\n    pollInterval: 100\n  }\n});\n\n// Update config content when file changes\nwatcher.on('change', async (path) => {\n  console.log('Config file changed');\n  currentConfig = await readConfig();\n  lastModified = new Date();\n});\n\n// Tool to get current config content\nserver.tool(\n  \"getConfig\",\n  \"Returns the current content of the Goose config file\",\n  {}, // no parameters needed\n  { readOnlyHint: true },\n  async () => ({\n    content: [{ \n      type: \"text\", \n      text: `Last modified: ${lastModified.toISOString()}\\n\\n${currentConfig}` \n    }],\n  })\n);\n\n// Tool to check if config has changed since last check\nserver.tool(\n  \"checkConfigChanged\",\n  \"Checks if the config file has changed since a given timestamp\",\n  { \n    timestamp: z.string().describe(\"ISO timestamp to check against\") \n  },\n  { readOnlyHint: true },\n  async ({ timestamp }) => {\n    const checkTime = new Date(timestamp);\n    const hasChanged = lastModified > checkTime;\n    return {\n      content: [{ \n        type: \"text\", \n        text: hasChanged \n          ? `Config has changed. Last modified: ${lastModified.toISOString()}` \n          : \"No changes since last check\"\n      }],\n    };\n  }\n);\n\n// Resource that provides the config content\nserver.resource(\n  \"config\",\n  \"config://goose\",\n  async (uri) => ({\n    contents: [{\n      uri: uri.href,\n      text: currentConfig\n    }]\n  })\n);\n\n// Initialize the config content\nreadConfig().then(content => {\n  currentConfig = content;\n  lastModified = new Date();\n  console.log('Initial config loaded');\n});\n\n// Start receiving messages on stdin and sending messages on stdout\nconst transport = new StdioServerTransport();\nawait server.connect(transport);","path":"/Users/zane/Development/mcp_goose_configwatcher/index.ts"}}}}]}
ole":"user","created":1747178389,"content":[{"type":"toolResponse","id":"toolu_bdrk_01NX8eskWWV81bAcRLPcPg1d","toolResult":{"status":"success","value":[{"type":"text","text":"Successfully wrote to /Users/zane/Development/mcp_goose_configwatcher/index.ts","annotations":{"audience":["assistant"]}},{"type":"text","text":"### /Users/zane/Development/mcp_goose_configwatcher/index.ts\n```typescript\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport * as chokidar from 'chokidar';\nimport * as fs from 'fs/promises';\n\nconst CONFIG_PATH = '/Users/zane/.config/goose/config.yaml';\n\n// Update the instructions for your MCP server\nconst instructions =\n  \"This MCP server watches the Goose config file for changes and provides tools to access its content.\";\n\n// Create an MCP server\nconst server = new McpServer(\n  {\n    name: \"goose_config_watcher\",\n    version: \"0.0.1\",\n  },\n  {\n    capabilities: {\n      tools: {},\n      resources: {},\n    },\n    instructions: instructions,\n  }\n);\n\n// Keep track of the current config content\nlet currentConfig = '';\nlet lastModified = new Date(0);\n\n// Function to read the config file\nasync function readConfig(): Promise<string> {\n  try {\n    return await fs.readFile(CONFIG_PATH, 'utf8');\n  } catch (error) {\n    console.error('Error reading config file:', error);\n    return '';\n  }\n}\n\n// Initialize the watcher\nconst watcher = chokidar.watch(CONFIG_PATH, {\n  persistent: true,\n  awaitWriteFinish: {\n    stabilityThreshold: 300,\n    pollInterval: 100\n  }\n});\n\n// Update config content when file changes\nwatcher.on('change', async (path) => {\n  console.log('Config file changed');\n  currentConfig = await readConfig();\n  lastModified = new Date();\n});\n\n// Tool to get current config content\nserver.tool(\n  \"getConfig\",\n  \"Returns the current content of the Goose config file\",\n  {}, // no parameters needed\n  { readOnlyHint: true },\n  async () => ({\n    content: [{ \n      type: \"text\", \n      text: `Last modified: ${lastModified.toISOString()}\\n\\n${currentConfig}` \n    }],\n  })\n);\n\n// Tool to check if config has changed since last check\nserver.tool(\n  \"checkConfigChanged\",\n  \"Checks if the config file has changed since a given timestamp\",\n  { \n    timestamp: z.string().describe(\"ISO timestamp to check against\") \n  },\n  { readOnlyHint: true },\n  async ({ timestamp }) => {\n    const checkTime = new Date(timestamp);\n    const hasChanged = lastModified > checkTime;\n    return {\n      content: [{ \n        type: \"text\", \n        text: hasChanged \n          ? `Config has changed. Last modified: ${lastModified.toISOString()}` \n          : \"No changes since last check\"\n      }],\n    };\n  }\n);\n\n// Resource that provides the config content\nserver.resource(\n  \"config\",\n  \"config://goose\",\n  async (uri) => ({\n    contents: [{\n      uri: uri.href,\n      text: currentConfig\n    }]\n  })\n);\n\n// Initialize the config content\nreadConfig().then(content => {\n  currentConfig = content;\n  lastModified = new Date();\n  console.log('Initial config loaded');\n});\n\n// Start receiving messages on stdin and sending messages on stdout\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n```\n","annotations":{"audience":["user"],"priority":0.2}}]}}]}

zanesq avatar May 13 '25 23:05 zanesq