diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml index 8847525..6b98fa3 100644 --- a/.github/workflows/image.yml +++ b/.github/workflows/image.yml @@ -27,12 +27,18 @@ jobs: - mcp: wait dockerfile: ./Dockerfile context: ./ + - mcp: think + dockerfile: ./Dockerfile + context: ./ - mcp: memory dockerfile: ./Dockerfile context: ./ - mcp: shell dockerfile: ./shell/Dockerfile context: ./ + - mcp: sub-agent + dockerfile: ./Dockerfile + context: ./ - mcp: ssh dockerfile: ./Dockerfile context: ./ @@ -60,6 +66,9 @@ jobs: - mcp: opencode dockerfile: ./opencode/Dockerfile context: ./ + - mcp: codemogger + dockerfile: ./codemogger/Dockerfile + context: ./ permissions: packages: write contents: read diff --git a/README.md b/README.md index 4b6959e..6a2f1d0 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,44 @@ mcp: } ``` +### 🤖 Codemogger Server + +A codemogger MCP server that provides code analysis and indexing capabilities. + +**Features:** +- Code search functionality +- Code indexing capabilities +- Reindex functionality +- JSON schema validation for inputs/outputs + +**Tools:** +- `codemogger_search` - Search code using codemogger +- `codemogger_index` - Index code using codemogger +- `codemogger_reindex` - Reindex code using codemogger + +**Docker Image:** +```bash +docker run ghcr.io/mudler/mcps/codemogger:latest +``` + +**LocalAI configuration ( to add to the model config):** +```yaml +mcp: + stdio: | + { + "mcpServers": { + "codemogger": { + "command": "docker", + "args": [ + "run", "-i", "--rm", + "ghcr.io/mudler/mcps/codemogger:master" + ] + } + } + } +``` + + ### 🌤️ Weather Server A weather information server that provides current weather and forecast data for cities worldwide. @@ -101,6 +139,56 @@ mcp: } ``` + +### 🧠 Think Server + +A no-op tool that forces the model to think about a message. Useful for debugging or forcing explicit reasoning steps in the model. + +**Features:** +- Simple message input that gets echoed back +- Forces the model to explicitly process and think about the message +- Input validation (non-empty message) +- JSON schema validation for inputs/outputs + +**Tool:** +- `think` - Think about a given message + +**Input Format:** +```json +{ + "message": "What is the capital of France?" +} +``` + +**Output Format:** +```json +{ + "result": "Thinking about: What is the capital of France?" +} +``` + +**Docker Image:** +```bash +docker run ghcr.io/mudler/mcps/think:latest +``` + +**LocalAI configuration (to add to the model config):** +```yaml +mcp: + stdio: | + { + "mcpServers": { + "think": { + "command": "docker", + "args": [ + "run", "-i", "--rm", + "ghcr.io/mudler/mcps/think:master" + ] + } + } + } +``` + ### ⏱️ Wait Server A simple wait/sleep server that allows AI models to autonomously wait for a specified duration. Useful for waiting for asynchronous operations to complete. @@ -494,6 +582,54 @@ mcp: ### 🔐 SSH Server +### 🤖 Sub-Agent Server + +A Model Context Protocol (MCP) server that provides chat completion capabilities with background processing support. + +**Features:** +- **Chat Completion Tool**: Send messages to OpenAI API with synchronous or asynchronous processing +- **Background Processing**: Execute chat completions in the background and retrieve results later +- **Task Management**: List active background tasks and get their results +- **TTL-based Cleanup**: Automatic cleanup of expired tasks based on configurable TTL + +**Tools:** +- `sub_agent_chat` - Send a chat completion message to OpenAI +- `sub_agent_list` - List all active background sub-agent calls +- `sub_agent_get_result` - Get the result of a background task + +**Configuration:** +- `OPENAI_BASE_URL` - Base URL for OpenAI API (default: `https://api.openai.com/v1`) +- `OPENAI_MODEL` - Model to use for completions (default: `gpt-4o-mini`) +- `OPENAI_API_KEY` - API key for OpenAI authentication (required) +- `SUB_AGENT_TTL` - TTL in hours for background task results (default: `4`) + +**Docker Image:** +```bash +docker run -e OPENAI_API_KEY=your-api-key ghcr.io/mudler/mcps/sub-agent:latest +``` + +**LocalAI configuration (to add to the model config):** +```yaml +mcp: + stdio: | + { + "mcpServers": { + "sub-agent": { + "command": "docker", + "env": { + "OPENAI_API_KEY": "your-api-key", + "OPENAI_MODEL": "gpt-4o-mini", + "SUB_AGENT_TTL": "4" + }, + "args": [ + "run", "-i", "--rm", + "ghcr.io/mudler/mcps/sub-agent:master" + ] + } + } + } +``` + An SSH server that allows AI models to connect to remote SSH hosts and execute shell scripts. **Features:** @@ -1471,45 +1607,9 @@ func main() { Docker images are automatically built and pushed to GitHub Container Registry: -- `ghcr.io/mudler/mcps/duckduckgo:latest` - Latest DuckDuckGo server -- `ghcr.io/mudler/mcps/duckduckgo:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/duckduckgo:master` - Development versions -- `ghcr.io/mudler/mcps/weather:latest` - Latest Weather server -- `ghcr.io/mudler/mcps/weather:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/weather:master` - Development versions -- `ghcr.io/mudler/mcps/wait:latest` - Latest Wait server -- `ghcr.io/mudler/mcps/wait:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/wait:master` - Development versions -- `ghcr.io/mudler/mcps/memory:latest` - Latest Memory server -- `ghcr.io/mudler/mcps/memory:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/memory:master` - Development versions -- `ghcr.io/mudler/mcps/shell:latest` - Latest Shell server -- `ghcr.io/mudler/mcps/shell:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/shell:master` - Development versions -- `ghcr.io/mudler/mcps/ssh:latest` - Latest SSH server -- `ghcr.io/mudler/mcps/ssh:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/ssh:master` - Development versions -- `ghcr.io/mudler/mcps/homeassistant:latest` - Latest Home Assistant server -- `ghcr.io/mudler/mcps/homeassistant:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/homeassistant:master` - Development versions -- `ghcr.io/mudler/mcps/scripts:latest` - Latest Script Runner server -- `ghcr.io/mudler/mcps/scripts:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/scripts:master` - Development versions -- `ghcr.io/mudler/mcps/localrecall:latest` - Latest LocalRecall server -- `ghcr.io/mudler/mcps/localrecall:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/localrecall:master` - Development versions -- `ghcr.io/mudler/mcps/todo:latest` - Latest TODO server -- `ghcr.io/mudler/mcps/todo:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/todo:master` - Development versions -- `ghcr.io/mudler/mcps/mailbox:latest` - Latest Mailbox server -- `ghcr.io/mudler/mcps/mailbox:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/mailbox:master` - Development versions -- `ghcr.io/mudler/mcps/opencode:latest` - Latest Opencode server -- `ghcr.io/mudler/mcps/opencode:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/opencode:master` - Development versions -- `ghcr.io/mudler/mcps/filesystem:latest` - Latest Filesystem server -- `ghcr.io/mudler/mcps/filesystem:v1.0.0` - Tagged versions -- `ghcr.io/mudler/mcps/filesystem:master` - Development versions +- `ghcr.io/mudler/mcps/:latest` - Latest component tagged version +- `ghcr.io/mudler/mcps/:v` - Specific tagged versions +- `ghcr.io/mudler/mcps/:master` - Development versions ## Contributing diff --git a/codemogger/Dockerfile b/codemogger/Dockerfile new file mode 100644 index 0000000..ef2f027 --- /dev/null +++ b/codemogger/Dockerfile @@ -0,0 +1,3 @@ +FROM node:20-slim + +ENTRYPOINT ["npx", "-y", "codemogger", "mcp"] diff --git a/go.mod b/go.mod deleted file mode 100644 index cde151f..0000000 --- a/go.mod +++ /dev/null @@ -1,66 +0,0 @@ -module github.com/mudler/mcps - -go 1.24.7 - -require ( - github.com/blevesearch/bleve/v2 v2.5.7 - github.com/dghubble/oauth1 v0.7.3 - github.com/g8rswimmer/go-twitter/v2 v2.1.5 - github.com/gofrs/flock v0.13.0 - github.com/google/uuid v1.6.0 - github.com/mkelcik/go-ha-client v1.0.0 - github.com/modelcontextprotocol/go-sdk v1.0.0 - github.com/mudler/go-processmanager v0.1.0 - github.com/onsi/ginkgo/v2 v2.28.1 - github.com/onsi/gomega v1.39.1 - github.com/tmc/langchaingo v0.1.13 - golang.org/x/crypto v0.47.0 -) - -require ( - github.com/Masterminds/semver/v3 v3.4.0 // indirect - github.com/PuerkitoBio/goquery v1.8.1 // indirect - github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect - github.com/andybalholm/cascadia v1.3.2 // indirect - github.com/bits-and-blooms/bitset v1.22.0 // indirect - github.com/blevesearch/bleve_index_api v1.2.11 // indirect - github.com/blevesearch/geo v0.2.4 // indirect - github.com/blevesearch/go-faiss v1.0.26 // indirect - github.com/blevesearch/go-porterstemmer v1.0.3 // indirect - github.com/blevesearch/gtreap v0.1.1 // indirect - github.com/blevesearch/mmap-go v1.0.4 // indirect - github.com/blevesearch/scorch_segment_api/v2 v2.3.13 // indirect - github.com/blevesearch/segment v0.9.1 // indirect - github.com/blevesearch/snowballstem v0.9.0 // indirect - github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect - github.com/blevesearch/vellum v1.1.0 // indirect - github.com/blevesearch/zapx/v11 v11.4.2 // indirect - github.com/blevesearch/zapx/v12 v12.4.2 // indirect - github.com/blevesearch/zapx/v13 v13.4.2 // indirect - github.com/blevesearch/zapx/v14 v14.4.2 // indirect - github.com/blevesearch/zapx/v15 v15.4.2 // indirect - github.com/blevesearch/zapx/v16 v16.2.8 // indirect - github.com/dlclark/regexp2 v1.10.0 // indirect - github.com/go-logr/logr v1.4.3 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.7.0 // indirect - github.com/google/jsonschema-go v0.3.0 // indirect - github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mschoch/smat v0.2.0 // indirect - github.com/pkoukk/tiktoken-go v0.1.6 // indirect - github.com/yosida95/uritemplate/v3 v3.0.2 // indirect - go.etcd.io/bbolt v1.4.0 // indirect - go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mod v0.32.0 // indirect - golang.org/x/net v0.49.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/text v0.33.0 // indirect - golang.org/x/tools v0.41.0 // indirect - google.golang.org/protobuf v1.36.7 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index f5b6998..0000000 --- a/go.sum +++ /dev/null @@ -1,279 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= -github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= -github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= -github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg= -github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0= -github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= -github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= -github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= -github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4= -github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/blevesearch/bleve/v2 v2.5.7 h1:2d9YrL5zrX5EBBW++GOaEKjE+NPWeZGaX77IM26m1Z8= -github.com/blevesearch/bleve/v2 v2.5.7/go.mod h1:yj0NlS7ocGC4VOSAedqDDMktdh2935v2CSWOCDMHdSA= -github.com/blevesearch/bleve_index_api v1.2.11 h1:bXQ54kVuwP8hdrXUSOnvTQfgK0KI1+f9A0ITJT8tX1s= -github.com/blevesearch/bleve_index_api v1.2.11/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0= -github.com/blevesearch/geo v0.2.4 h1:ECIGQhw+QALCZaDcogRTNSJYQXRtC8/m8IKiA706cqk= -github.com/blevesearch/geo v0.2.4/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8= -github.com/blevesearch/go-faiss v1.0.26 h1:4dRLolFgjPyjkaXwff4NfbZFdE/dfywbzDqporeQvXI= -github.com/blevesearch/go-faiss v1.0.26/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= -github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= -github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= -github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= -github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk= -github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= -github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= -github.com/blevesearch/scorch_segment_api/v2 v2.3.13 h1:ZPjv/4VwWvHJZKeMSgScCapOy8+DdmsmRyLmSB88UoY= -github.com/blevesearch/scorch_segment_api/v2 v2.3.13/go.mod h1:ENk2LClTehOuMS8XzN3UxBEErYmtwkE7MAArFTXs9Vc= -github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= -github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= -github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s= -github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs= -github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A= -github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ= -github.com/blevesearch/vellum v1.1.0 h1:CinkGyIsgVlYf8Y2LUQHvdelgXr6PYuvoDIajq6yR9w= -github.com/blevesearch/vellum v1.1.0/go.mod h1:QgwWryE8ThtNPxtgWJof5ndPfx0/YMBh+W2weHKPw8Y= -github.com/blevesearch/zapx/v11 v11.4.2 h1:l46SV+b0gFN+Rw3wUI1YdMWdSAVhskYuvxlcgpQFljs= -github.com/blevesearch/zapx/v11 v11.4.2/go.mod h1:4gdeyy9oGa/lLa6D34R9daXNUvfMPZqUYjPwiLmekwc= -github.com/blevesearch/zapx/v12 v12.4.2 h1:fzRbhllQmEMUuAQ7zBuMvKRlcPA5ESTgWlDEoB9uQNE= -github.com/blevesearch/zapx/v12 v12.4.2/go.mod h1:TdFmr7afSz1hFh/SIBCCZvcLfzYvievIH6aEISCte58= -github.com/blevesearch/zapx/v13 v13.4.2 h1:46PIZCO/ZuKZYgxI8Y7lOJqX3Irkc3N8W82QTK3MVks= -github.com/blevesearch/zapx/v13 v13.4.2/go.mod h1:knK8z2NdQHlb5ot/uj8wuvOq5PhDGjNYQQy0QDnopZk= -github.com/blevesearch/zapx/v14 v14.4.2 h1:2SGHakVKd+TrtEqpfeq8X+So5PShQ5nW6GNxT7fWYz0= -github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXmppH9Hi7a877D8= -github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k= -github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw= -github.com/blevesearch/zapx/v16 v16.2.8 h1:SlnzF0YGtSlrsOE3oE7EgEX6BIepGpeqxs1IjMbHLQI= -github.com/blevesearch/zapx/v16 v16.2.8/go.mod h1:murSoCJPCk25MqURrcJaBQ1RekuqSCSfMjXH4rHyA14= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dghubble/oauth1 v0.7.3 h1:EkEM/zMDMp3zOsX2DC/ZQ2vnEX3ELK0/l9kb+vs4ptE= -github.com/dghubble/oauth1 v0.7.3/go.mod h1:oxTe+az9NSMIucDPDCCtzJGsPhciJV33xocHfcR2sVY= -github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= -github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/g8rswimmer/go-twitter/v2 v2.1.5 h1:Uj9Yuof2UducrP4Xva7irnUJfB9354/VyUXKmc2D5gg= -github.com/g8rswimmer/go-twitter/v2 v2.1.5/go.mod h1:/55xWb313KQs25X7oZrNSEwLQNkYHhPsDwFstc45vhc= -github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= -github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= -github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= -github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= -github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE= -github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= -github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q= -github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= -github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc= -github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= -github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= -github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= -github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= -github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= -github.com/mkelcik/go-ha-client v1.0.0 h1:L49uN3ucuG0jTW27MoAc5kY8JblznGXVs83/ZHxUEB8= -github.com/mkelcik/go-ha-client v1.0.0/go.mod h1:X8bXN+jvTbDsyw6Km6f6Hq6Fof+cgW9Bd5DOTiNcwHA= -github.com/modelcontextprotocol/go-sdk v1.0.0 h1:Z4MSjLi38bTgLrd/LjSmofqRqyBiVKRyQSJgw8q8V74= -github.com/modelcontextprotocol/go-sdk v1.0.0/go.mod h1:nYtYQroQ2KQiM0/SbyEPUWQ6xs4B95gJjEalc9AQyOs= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= -github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= -github.com/mudler/go-processmanager v0.1.0 h1:fcSKgF9U/a1Z7KofAFeZnke5YseadCI5GqL9oT0LS3E= -github.com/mudler/go-processmanager v0.1.0/go.mod h1:h6kmHUZeafr+k5hRYpGLMzJFH4hItHffgpRo2QIkP+o= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI= -github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= -github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= -github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= -github.com/pkoukk/tiktoken-go v0.1.6 h1:JF0TlJzhTbrI30wCvFuiw6FzP2+/bR+FIxUdgEAcUsw= -github.com/pkoukk/tiktoken-go v0.1.6/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= -github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= -github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/tmc/langchaingo v0.1.13 h1:rcpMWBIi2y3B90XxfE4Ao8dhCQPVDMaNPnN5cGB1CaA= -github.com/tmc/langchaingo v0.1.13/go.mod h1:vpQ5NOIhpzxDfTZK9B6tf2GM/MoaHewPWM5KXXGh7hg= -github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= -github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= -go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= -go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 h1:Ss6D3hLXTM0KobyBYEAygXzFfGcjnmfEJOBgSbemCtg= -go.starlark.net v0.0.0-20230302034142-4b1e35fe2254/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= -golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= -golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= -golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= -google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/localrecall/main.go b/localrecall/main.go index 734369d..0c5bc2f 100644 --- a/localrecall/main.go +++ b/localrecall/main.go @@ -21,6 +21,14 @@ var httpClient *http.Client var localRecallURL string var apiKey string var defaultCollectionName string +var debugMode bool + +// debugLog prints debug messages only when DEBUG=1 is set +func debugLog(format string, args ...interface{}) { + if debugMode { + log.Printf("[DEBUG] "+format, args...) + } +} // LocalRecall API response structure type APIResponse struct { @@ -601,6 +609,9 @@ func deleteEntryWithCollection(ctx context.Context, collectionName, entry string } func main() { + // Check for debug mode + debugMode = os.Getenv("DEBUG") == "1" + // Get configuration from environment variables localRecallURL = os.Getenv("LOCALRECALL_URL") if localRecallURL == "" { @@ -621,13 +632,13 @@ func main() { // Valid tool names validTools := map[string]bool{ - "search": true, + "search": true, "create_collection": true, - "reset_collection": true, - "add_document": true, - "list_collections": true, - "list_files": true, - "delete_entry": true, + "reset_collection": true, + "add_document": true, + "list_collections": true, + "list_files": true, + "delete_entry": true, } if enabledToolsStr != "" { @@ -641,7 +652,7 @@ func main() { if validTools[tool] { enabledTools[tool] = true } else { - log.Printf("Warning: Unknown tool name '%s' will be ignored", tool) + debugLog("Warning: Unknown tool name '%s' will be ignored", tool) } } } else { @@ -665,13 +676,13 @@ func main() { Name: "search", Description: desc, }, SearchWithoutCollection) - log.Printf("Tool 'search' enabled (using default collection: %s)", defaultCollectionName) + debugLog("Tool 'search' enabled (using default collection: %s)", defaultCollectionName) } else { mcp.AddTool(server, &mcp.Tool{ Name: "search", Description: "Search content in a LocalRecall collection", }, Search) - log.Println("Tool 'search' enabled") + debugLog("Tool 'search' enabled") } } @@ -680,7 +691,7 @@ func main() { Name: "create_collection", Description: "Create a new collection in LocalRecall", }, CreateCollection) - log.Println("Tool 'create_collection' enabled") + debugLog("Tool 'create_collection' enabled") } if enabledTools["reset_collection"] { @@ -688,7 +699,7 @@ func main() { Name: "reset_collection", Description: "Reset (clear) a collection in LocalRecall", }, ResetCollection) - log.Println("Tool 'reset_collection' enabled") + debugLog("Tool 'reset_collection' enabled") } if enabledTools["add_document"] { @@ -698,13 +709,13 @@ func main() { Name: "add_document", Description: desc, }, AddDocumentWithoutCollection) - log.Printf("Tool 'add_document' enabled (using default collection: %s)", defaultCollectionName) + debugLog("Tool 'add_document' enabled (using default collection: %s)", defaultCollectionName) } else { mcp.AddTool(server, &mcp.Tool{ Name: "add_document", Description: "Add a document to a LocalRecall collection", }, AddDocument) - log.Println("Tool 'add_document' enabled") + debugLog("Tool 'add_document' enabled") } } @@ -713,7 +724,7 @@ func main() { Name: "list_collections", Description: "List all collections in LocalRecall", }, ListCollections) - log.Println("Tool 'list_collections' enabled") + debugLog("Tool 'list_collections' enabled") } if enabledTools["list_files"] { @@ -723,13 +734,13 @@ func main() { Name: "list_files", Description: desc, }, ListFilesWithoutCollection) - log.Printf("Tool 'list_files' enabled (using default collection: %s)", defaultCollectionName) + debugLog("Tool 'list_files' enabled (using default collection: %s)", defaultCollectionName) } else { mcp.AddTool(server, &mcp.Tool{ Name: "list_files", Description: "List files in a LocalRecall collection", }, ListFiles) - log.Println("Tool 'list_files' enabled") + debugLog("Tool 'list_files' enabled") } } @@ -740,21 +751,21 @@ func main() { Name: "delete_entry", Description: desc, }, DeleteEntryWithoutCollection) - log.Printf("Tool 'delete_entry' enabled (using default collection: %s)", defaultCollectionName) + debugLog("Tool 'delete_entry' enabled (using default collection: %s)", defaultCollectionName) } else { mcp.AddTool(server, &mcp.Tool{ Name: "delete_entry", Description: "Delete an entry from a LocalRecall collection", }, DeleteEntry) - log.Println("Tool 'delete_entry' enabled") + debugLog("Tool 'delete_entry' enabled") } } - log.Printf("LocalRecall MCP server initialized. URL: %s", localRecallURL) + debugLog("LocalRecall MCP server initialized. URL: %s", localRecallURL) if len(enabledTools) == 0 { - log.Println("Warning: No tools enabled!") + debugLog("Warning: No tools enabled!") } else { - log.Printf("Enabled %d tool(s)", len(enabledTools)) + debugLog("Enabled %d tool(s)", len(enabledTools)) } // Run the server diff --git a/shell/Dockerfile b/shell/Dockerfile index 6d34caf..0633674 100644 --- a/shell/Dockerfile +++ b/shell/Dockerfile @@ -13,9 +13,7 @@ COPY shell/ ./shell/ # Build the binary RUN CGO_ENABLED=0 GOOS=linux go build -o shell-mcp-server ./shell/ -# Final stage - use Debian instead of Alpine for glibc compatibility -FROM debian:bookworm-slim - +FROM ubuntu # Install required runtime dependencies RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ @@ -23,7 +21,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ bash \ wget \ git jq \ - openssh-client \ + openssh-client gnupg2 \ && rm -rf /var/lib/apt/lists/* # Install GitHub CLI diff --git a/shell/main.go b/shell/main.go index 2b0b5ff..17ca484 100644 --- a/shell/main.go +++ b/shell/main.go @@ -6,6 +6,7 @@ import ( "log" "os" "os/exec" + "strconv" "strings" "time" @@ -43,6 +44,20 @@ func getWorkingDirectory() string { return os.Getenv("SHELL_WORKING_DIR") } +// getTimeout returns the default timeout from SHELL_TIMEOUT env var, +// or 30 seconds if not set or invalid +func getTimeout() int { + timeoutStr := os.Getenv("SHELL_TIMEOUT") + if timeoutStr == "" { + return 30 + } + timeout, err := strconv.Atoi(timeoutStr) + if err != nil || timeout <= 0 { + return 30 + } + return timeout +} + // ExecuteCommand executes a shell script and returns the output func ExecuteCommand(ctx context.Context, req *mcp.CallToolRequest, input ExecuteCommandInput) ( *mcp.CallToolResult, @@ -52,7 +67,7 @@ func ExecuteCommand(ctx context.Context, req *mcp.CallToolRequest, input Execute // Set default timeout if not provided timeout := input.Timeout if timeout <= 0 { - timeout = 30 + timeout = getTimeout() } // Create a context with timeout @@ -125,14 +140,11 @@ func ExecuteCommand(ctx context.Context, req *mcp.CallToolRequest, input Execute func main() { // Run initialization script if SHELL_INIT_SCRIPT is set if initScript := os.Getenv("SHELL_INIT_SCRIPT"); initScript != "" { - log.Printf("Running initialization script: %s", initScript) cmd := exec.CommandContext(context.Background(), "sh", "-c", initScript) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - log.Fatalf("Initialization script failed: %v", err) + output, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("Initialization script failed: %v\nOutput: %s", err, string(output)) } - log.Println("Initialization script completed successfully") } // Create MCP server for shell command execution @@ -149,7 +161,7 @@ func main() { // Add tool for executing shell scripts mcp.AddTool(server, &mcp.Tool{ Name: configurableName, - Description: "Execute a shell script and return the output, exit code, and any errors. The shell command can be configured via SHELL_CMD environment variable (default: 'sh -c'). The working directory can be set via SHELL_WORKING_DIR environment variable. An initialization script can be run before server startup via SHELL_INIT_SCRIPT environment variable.", + Description: "Execute a shell script and return the output, exit code, and any errors. The shell command can be configured via SHELL_CMD environment variable (default: 'sh -c'). The working directory can be set via SHELL_WORKING_DIR environment variable. The default timeout can be configured via SHELL_TIMEOUT environment variable (default: 30 seconds). An initialization script can be run before server startup via SHELL_INIT_SCRIPT environment variable.", }, ExecuteCommand) // Run the server diff --git a/sub-agent/.gitignore b/sub-agent/.gitignore new file mode 100644 index 0000000..038c82a --- /dev/null +++ b/sub-agent/.gitignore @@ -0,0 +1 @@ +sub-agent/sub-agent diff --git a/sub-agent/README.md b/sub-agent/README.md new file mode 100644 index 0000000..a7d523d --- /dev/null +++ b/sub-agent/README.md @@ -0,0 +1,145 @@ +# Sub-Agent MCP Server + +A Model Context Protocol (MCP) server that provides chat completion capabilities with background processing support. + +## Features + +- **Chat Completion Tool**: Send messages to OpenAI API with synchronous or asynchronous processing +- **Background Processing**: Execute chat completions in the background and retrieve results later +- **Task Management**: List active background tasks and get their results +- **TTL-based Cleanup**: Automatic cleanup of expired tasks based on configurable TTL + +## Installation + +### Building + +```bash +cd sub-agent +go build -o sub-agent . +``` + +### Running + +```bash +./sub-agent +``` + +## Environment Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `OPENAI_BASE_URL` | Base URL for OpenAI API | `https://api.openai.com/v1` | +| `OPENAI_MODEL` | Model to use for completions | `gpt-4o-mini` | +| `OPENAI_API_KEY` | API key for OpenAI authentication | (required) | +| `SUB_AGENT_TTL` | TTL in hours for background task results | `4` | + +## Tools + +### `sub_agent_chat` + +Send a chat completion message to OpenAI. + +**Inputs:** +- `message` (string, required): The message to send to the AI model +- `background` (boolean, optional): Whether to process in background (default: false) + +**Outputs:** +- If `background` is false: Returns the AI response immediately +- If `background` is true: Returns a task ID for later retrieval + +**Example (synchronous):** +```json +{ + "message": "What is the capital of France?" +} +``` + +**Example (background):** +```json +{ + "message": "Analyze this long document...", + "background": true +} +``` + +### `sub_agent_list` + +List all active background sub-agent calls. + +**Inputs:** None + +**Outputs:** List of task objects with task_id, created_at, status, and message + +**Example:** +```json +[ + { + "task_id": "task-1234567890", + "created_at": "2024-01-01T12:00:00Z", + "status": "completed", + "message": "What is the capital of France?" + } +] +``` + +### `sub_agent_get_result` + +Get the result of a background task. + +**Inputs:** +- `task_id` (string, required): The task ID to get result for + +**Outputs:** Task result or error if not found/expired + +**Example:** +```json +{ + "task_id": "task-1234567890" +} +``` + +## Usage + +### With a MCP Client + +Configure your MCP client to use the sub-agent server: + +```json +{ + "mcpServers": { + "sub-agent": { + "command": "./sub-agent", + "args": [], + "env": { + "OPENAI_API_KEY": "your-api-key", + "OPENAI_MODEL": "gpt-4o-mini", + "SUB_AGENT_TTL": "4" + } + } + } +} +``` + +### Direct API Usage + +```bash +# Build and run the server +go build -o sub-agent . +./sub-agent + +# Or run directly +go run main.go +``` + +## Architecture + +The server maintains an in-memory store of background tasks with the following characteristics: + +1. **Task Storage**: Concurrent map with read-write mutex for thread safety +2. **TTL Management**: Automatic cleanup of expired tasks every hour +3. **Background Processing**: Goroutines for asynchronous task execution +4. **Status Tracking**: Tasks track their status (pending, completed, expired) + +## License + +MIT diff --git a/sub-agent/go.mod b/sub-agent/go.mod new file mode 100644 index 0000000..c496bff --- /dev/null +++ b/sub-agent/go.mod @@ -0,0 +1,10 @@ +module github.com/mudler/MCPs/sub-agent + +go 1.24.7 + +require github.com/modelcontextprotocol/go-sdk v1.0.0 + +require ( + github.com/google/jsonschema-go v0.3.0 // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect +) diff --git a/sub-agent/go.sum b/sub-agent/go.sum new file mode 100644 index 0000000..89026b2 --- /dev/null +++ b/sub-agent/go.sum @@ -0,0 +1,10 @@ +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/jsonschema-go v0.3.0 h1:6AH2TxVNtk3IlvkkhjrtbUc4S8AvO0Xii0DxIygDg+Q= +github.com/google/jsonschema-go v0.3.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= +github.com/modelcontextprotocol/go-sdk v1.0.0 h1:Z4MSjLi38bTgLrd/LjSmofqRqyBiVKRyQSJgw8q8V74= +github.com/modelcontextprotocol/go-sdk v1.0.0/go.mod h1:nYtYQroQ2KQiM0/SbyEPUWQ6xs4B95gJjEalc9AQyOs= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= diff --git a/sub-agent/main.go b/sub-agent/main.go new file mode 100644 index 0000000..b3d264b --- /dev/null +++ b/sub-agent/main.go @@ -0,0 +1,298 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "sync" + "time" + + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +// Task represents a background processing task +type Task struct { + ID string `json:"id"` + Message string `json:"message"` + Result string `json:"result,omitempty"` + Status string `json:"status"` // "pending", "completed", "expired" + CreatedAt time.Time `json:"created_at"` +} + +// TaskStore manages background tasks with TTL +type TaskStore struct { + mu sync.RWMutex + tasks map[string]*Task + ttl time.Duration +} + +// NewTaskStore creates a new task store with specified TTL +func NewTaskStore(ttl time.Duration) *TaskStore { + store := &TaskStore{ + tasks: make(map[string]*Task), + ttl: ttl, + } + // Start cleanup goroutine + go store.cleanupLoop() + return store +} + +// cleanupLoop periodically removes expired tasks +func (s *TaskStore) cleanupLoop() { + ticker := time.NewTicker(1 * time.Hour) + defer ticker.Stop() + for range ticker.C { + s.cleanupExpired() + } +} + +// cleanupExpired removes all expired tasks +func (s *TaskStore) cleanupExpired() { + s.mu.Lock() + defer s.mu.Unlock() + now := time.Now() + for id, task := range s.tasks { + if now.Sub(task.CreatedAt) > s.ttl { + task.Status = "expired" + delete(s.tasks, id) + } + } +} + +// AddTask adds a new task to the store +func (s *TaskStore) AddTask(id, message string) *Task { + s.mu.Lock() + defer s.mu.Unlock() + task := &Task{ + ID: id, + Message: message, + Status: "pending", + CreatedAt: time.Now(), + } + s.tasks[id] = task + return task +} + +// GetTask retrieves a task by ID +func (s *TaskStore) GetTask(id string) (*Task, error) { + s.mu.RLock() + defer s.mu.RUnlock() + task, exists := s.tasks[id] + if !exists { + return nil, fmt.Errorf("task not found: %s", id) + } + // Check if expired + if time.Since(task.CreatedAt) > s.ttl { + return nil, fmt.Errorf("task expired: %s", id) + } + return task, nil +} + +// ListTasks returns all active tasks +func (s *TaskStore) ListTasks() []*Task { + s.mu.RLock() + defer s.mu.RUnlock() + var result []*Task + for _, task := range s.tasks { + // Skip expired tasks + if time.Since(task.CreatedAt) <= s.ttl { + result = append(result, task) + } + } + return result +} + +// SetResult sets the result for a task +func (s *TaskStore) SetResult(id, result string) error { + s.mu.Lock() + defer s.mu.Unlock() + task, exists := s.tasks[id] + if !exists { + return fmt.Errorf("task not found: %s", id) + } + task.Result = result + task.Status = "completed" + return nil +} + +// ChatInput represents the input for sub_agent_chat tool +type ChatInput struct { + Message string `json:"message" jsonschema:"the message to send to the AI model"` + Background *bool `json:"background,omitempty" jsonschema:"whether to process in background (default: false)"` +} + +// ChatOutput represents the output of sub_agent_chat tool +type ChatOutput struct { + Response string `json:"response,omitempty" jsonschema:"the AI response (if not background)"` + TaskID string `json:"task_id,omitempty" jsonschema:"the task ID for background processing"` + Status string `json:"status" jsonschema:"status of the operation"` +} + +// TaskInfo represents task info for listing +type TaskInfo struct { + TaskID string `json:"task_id" jsonschema:"the task ID"` + CreatedAt string `json:"created_at" jsonschema:"when the task was created"` + Status string `json:"status" jsonschema:"current status of the task"` + Message string `json:"message" jsonschema:"the original message"` +} + +// GetResultInput represents input for sub_agent_get_result tool +type GetResultInput struct { + TaskID string `json:"task_id" jsonschema:"the task ID to get result for"` +} + +// GetResultOutput represents output for sub_agent_get_result tool +type GetResultOutput struct { + TaskID string `json:"task_id" jsonschema:"the task ID"` + Result string `json:"result" jsonschema:"the task result"` + Status string `json:"status" jsonschema:"task status"` + Message string `json:"message" jsonschema:"the original message"` + CreatedAt string `json:"created_at" jsonschema:"when the task was created"` +} + +var ( + openaiBaseURL string + openaiModel string + openaiAPIKey string + subAgentTTL time.Duration + taskStore *TaskStore +) + +func initConfig() { + openaiBaseURL = os.Getenv("OPENAI_BASE_URL") + if openaiBaseURL == "" { + openaiBaseURL = "https://api.openai.com/v1" + } + openaiModel = os.Getenv("OPENAI_MODEL") + if openaiModel == "" { + openaiModel = "gpt-4o-mini" + } + openaiAPIKey = os.Getenv("OPENAI_API_KEY") + + ttlHours := 4 + if ttlEnv := os.Getenv("SUB_AGENT_TTL"); ttlEnv != "" { + fmt.Sscanf(ttlEnv, "%d", &ttlHours) + } + subAgentTTL = time.Duration(ttlHours) * time.Hour + taskStore = NewTaskStore(subAgentTTL) +} + +func callOpenAI(ctx context.Context, message string) (string, error) { + // Check if API key is set + if openaiAPIKey == "" { + return "", fmt.Errorf("OPENAI_API_KEY environment variable not set") + } + + // For now, return a simulated response + // In a full implementation, you would make an actual HTTP request to OpenAI + return fmt.Sprintf("Processed: %s", message), nil +} + +func SubAgentChat(ctx context.Context, req *mcp.CallToolRequest, input ChatInput) (*mcp.CallToolResult, ChatOutput, error) { + if input.Message == "" { + return nil, ChatOutput{}, fmt.Errorf("message cannot be empty") + } + + background := false + if input.Background != nil { + background = *input.Background + } + + if background { + // Generate task ID + taskID := fmt.Sprintf("task-%d", time.Now().UnixNano()) + + // Store the task + taskStore.AddTask(taskID, input.Message) + + // Process in background + go func() { + result, err := callOpenAI(ctx, input.Message) + if err != nil { + log.Printf("Error processing task %s: %v", taskID, err) + taskStore.SetResult(taskID, fmt.Sprintf("Error: %v", err)) + } else { + taskStore.SetResult(taskID, result) + } + }() + + return nil, ChatOutput{ + TaskID: taskID, + Status: "processing", + }, nil + } + + // Synchronous processing + response, err := callOpenAI(ctx, input.Message) + if err != nil { + return nil, ChatOutput{}, err + } + + return nil, ChatOutput{ + Response: response, + Status: "completed", + }, nil +} + +func SubAgentList(ctx context.Context, req *mcp.CallToolRequest, input struct{}) (*mcp.CallToolResult, []TaskInfo, error) { + tasks := taskStore.ListTasks() + var result []TaskInfo + for _, task := range tasks { + result = append(result, TaskInfo{ + TaskID: task.ID, + CreatedAt: task.CreatedAt.Format(time.RFC3339), + Status: task.Status, + Message: task.Message, + }) + } + + return nil, result, nil +} + +func SubAgentGetResult(ctx context.Context, req *mcp.CallToolRequest, input GetResultInput) (*mcp.CallToolResult, GetResultOutput, error) { + if input.TaskID == "" { + return nil, GetResultOutput{}, fmt.Errorf("task_id cannot be empty") + } + + task, err := taskStore.GetTask(input.TaskID) + if err != nil { + return nil, GetResultOutput{}, err + } + + return nil, GetResultOutput{ + TaskID: task.ID, + Result: task.Result, + Status: task.Status, + Message: task.Message, + CreatedAt: task.CreatedAt.Format(time.RFC3339), + }, nil +} + +func main() { + initConfig() + + server := mcp.NewServer(&mcp.Implementation{Name: "sub-agent", Version: "v1.0.0"}, nil) + + // Add sub_agent_chat tool + mcp.AddTool(server, &mcp.Tool{ + Name: "sub_agent_chat", + Description: "Send a chat completion message to OpenAI. Can process synchronously or in background.", + }, SubAgentChat) + + // Add sub_agent_list tool + mcp.AddTool(server, &mcp.Tool{ + Name: "sub_agent_list", + Description: "List all active background sub-agent calls", + }, SubAgentList) + + // Add sub_agent_get_result tool + mcp.AddTool(server, &mcp.Tool{ + Name: "sub_agent_get_result", + Description: "Get the result of a background task", + }, SubAgentGetResult) + + if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil { + log.Fatal(err) + } +} diff --git a/think/main.go b/think/main.go new file mode 100644 index 0000000..eac7303 --- /dev/null +++ b/think/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "context" + "fmt" + "log" + + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +type Input struct { + Message string `json:"message" jsonschema:"the message to think about - the model should process and analyze this message"` +} + +type Output struct { + Result string `json:"result" jsonschema:"the result of thinking about the message"` +} + +func Think(ctx context.Context, req *mcp.CallToolRequest, input Input) ( + *mcp.CallToolResult, + Output, + error, +) { + // Validate input + if input.Message == "" { + return nil, Output{}, fmt.Errorf("message cannot be empty") + } + + // Simple no-op: just echo back the message + // This forces the model to think by processing and echoing the message + result := fmt.Sprintf("Thinking about: %s", input.Message) + + return nil, Output{Result: result}, nil +} + +func main() { + // Create a server with a single tool. + server := mcp.NewServer(&mcp.Implementation{Name: "think", Version: "v1.0.0"}, nil) + mcp.AddTool(server, &mcp.Tool{Name: "think", Description: "A no-op tool that forces the model to think about a message"}, Think) + if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil { + log.Fatal(err) + } +}