Local branch had diverged from origin/master (sibling commits on the same base). Rewrote local history linearly on top of origin/master, folding in all local content: exa/hot-platform discovery hunters, leaks ledger, nightly sweep orchestrator, updated .gitignore and skill docs, plus the latest hunt output and vault state. Remote-only files (vault keys, channel scripts) were restored rather than dropped, so the resulting tree is a full union of both sides.
1 line
132 KiB
JSON
1 line
132 KiB
JSON
{"repo": "archestra-ai/archestra", "files": {"platform/scripts/e2e-lite-platform.env": {"f41ed6e2f62c4034a03f4847a14f365244631687": {"t": 1785674026.98375, "n": 4259, "h": "8aa3e475f0569f36", "c": "# Platform container environment for the lite e2e harness (docker --env-file).\n#\n# Mirrors `archestra.env` in .github/values-ci.yaml (the Kind+Helm CI job).\n# Keep the two in sync: chat.spec.ts silently skips providers whose base URL\n# is not configured, so a provider added there but not here loses coverage\n# without failing anything.\n#\n# Vault settings are deliberately absent: the lite environment runs the\n# default DB-backed secrets manager; Vault specs live in the K8s CI job.\n\n# Quickstart mode: single container with internal Postgres and an embedded\n# Kind cluster (via the mounted docker socket) for the MCP orchestrator.\nARCHESTRA_QUICKSTART=true\n# No spec in this environment exercises the skill sandbox, so skip the Dagger\n# engine boot — this also lets CI run the -slim image (no dagger-engine.tar).\nARCHESTRA_CODE_RUNTIME_ENABLED=false\n\nENABLE_E2E_TEST_ENDPOINTS=true\nENABLE_TEST_MCP_SERVER=true\nARCHESTRA_ANALYTICS=disabled\n\n# LLM provider base URLs -> WireMock sidecar (per-provider path prefixes)\nARCHESTRA_OPENAI_BASE_URL=http://e2e-tests-wiremock:8080/openai/v1\nARCHESTRA_ANTHROPIC_BASE_URL=http://e2e-tests-wiremock:8080/anthropic\nARCHESTRA_GEMINI_BASE_URL=http://e2e-tests-wiremock:8080/gemini\nARCHESTRA_COHERE_BASE_URL=http://e2e-tests-wiremock:8080/cohere\nARCHESTRA_CEREBRAS_BASE_URL=http://e2e-tests-wiremock:8080/cerebras/v1\nARCHESTRA_VLLM_BASE_URL=http://e2e-tests-wiremock:8080/vllm/v1\nARCHESTRA_OLLAMA_BASE_URL=http://e2e-tests-wiremock:8080/ollama/v1\nARCHESTRA_GROQ_BASE_URL=http://e2e-tests-wiremock:8080/groq/v1\nARCHESTRA_XAI_BASE_URL=http://e2e-tests-wiremock:8080/xai/v1\nARCHESTRA_OPENROUTER_BASE_URL=http://e2e-tests-wiremock:8080/openrouter/api/v1\nARCHESTRA_ZHIPUAI_BASE_URL=http://e2e-tests-wiremock:8080/zhipuai/v4\nARCHESTRA_MISTRAL_BASE_URL=http://e2e-tests-wiremock:8080/mistral/v1\nARCHESTRA_BEDROCK_BASE_URL=http://e2e-tests-wiremock:8080/bedrock\nARCHESTRA_PERPLEXITY_BASE_URL=http://e2e-tests-wiremock:8080/perplexity/v1\nARCHESTRA_DEEPSEEK_BASE_URL=http://e2e-tests-wiremock:8080/deepseek\nARCHESTRA_GITHUB_COPILOT_BASE_URL=http://e2e-tests-wiremock:8080/github-copilot\nARCHESTRA_GITHUB_COPILOT_TOKEN_EXCHANGE_URL=http://e2e-tests-wiremock:8080/github/copilot_internal/v2/token\nARCHESTRA_MICROSOFT_365_COPILOT_BASE_URL=http://e2e-tests-wiremock:8080/microsoft-365-copilot\nARCHESTRA_MICROSOFT_365_COPILOT_AUTH_BASE_URL=http://e2e-tests-wiremock:8080/microsoft-login\nARCHESTRA_MICROSOFT_365_COPILOT_CLIENT_ID=e2e-test-client-id\nARCHESTRA_MINIMAX_BASE_URL=http://e2e-tests-wiremock:8080/minimax/v1\nARCHESTRA_AZURE_OPENAI_BASE_URL=http://e2e-tests-wiremock:8080/azure/openai/deployments/test-deployment\nARCHESTRA_AZURE_OPENAI_API_VERSION=2024-02-01\n\n# Chat API keys (chat routes require a key to be present)\nARCHESTRA_CHAT_OPENAI_API_KEY=test-key\nARCHESTRA_CHAT_ANTHROPIC_API_KEY=test-key\nARCHESTRA_CHAT_GEMINI_API_KEY=test-key\nARCHESTRA_CHAT_COHERE_API_KEY=test-key\nARCHESTRA_CHAT_CEREBRAS_API_KEY=test-key\nARCHESTRA_CHAT_VLLM_API_KEY=test-key\nARCHESTRA_CHAT_OLLAMA_API_KEY=test-key\nARCHESTRA_CHAT_GROQ_API_KEY=test-key\nARCHESTRA_CHAT_XAI_API_KEY=test-key\nARCHESTRA_CHAT_OPENROUTER_API_KEY=test-key\nARCHESTRA_CHAT_ZHIPUAI_API_KEY=test-key\nARCHESTRA_CHAT_MISTRAL_API_KEY=test-key\nARCHESTRA_CHAT_BEDROCK_API_KEY=test-key\nARCHESTRA_CHAT_PERPLEXITY_API_KEY=test-key\nARCHESTRA_CHAT_DEEPSEEK_API_KEY=test-key\nARCHESTRA_CHAT_GITHUB_COPILOT_API_KEY=test-key\nARCHESTRA_CHAT_MINIMAX_API_KEY=test-key\nARCHESTRA_CHAT_AZURE_OPENAI_API_KEY=test-key\n\nARCHESTRA_METRICS_SECRET=foo-bar\nARCHESTRA_FRONTEND_URL=http://localhost:3000\nARCHESTRA_AUTH_SECRET=e2e-auth-secret-must-be-32-chars-long\n\n# Enterprise license flags (required for the .ee specs)\nARCHESTRA_ENTERPRISE_LICENSE_ACTIVATED=true\nARCHESTRA_ENTERPRISE_LICENSE_FULL_WHITE_LABELING=true\nARCHESTRA_ENTERPRISE_LICENSE_KNOWLEDGE_BASE_ACTIVATED=true\n\n# Beta feature gates exercised by the suite\nARCHESTRA_KNOWLEDGE_BASE_AUTO_SYNC_PERMISSIONS_ENABLED=true\n\nARCHESTRA_AUTH_RATE_LIMIT_DISABLED=true\n\n# Keycloak sidecar as trusted origin (OIDC discovery URL validation):\n# the docker network name the backend resolves, and the host-published URL\n# that appears in OIDC discovery documents.\nARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS=http://e2e-tests-keycloak:8080,http://localhost:30081\n"}, "8c7c2dbffbd7e51833d663376b4692273cbaeaf0": {"t": 1785846816.5376978, "n": 4259, "h": "8aa3e475f0569f36", "c": "# Platform container environment for the lite e2e harness (docker --env-file).\n#\n# Mirrors `archestra.env` in .github/values-ci.yaml (the Kind+Helm CI job).\n# Keep the two in sync: chat.spec.ts silently skips providers whose base URL\n# is not configured, so a provider added there but not here loses coverage\n# without failing anything.\n#\n# Vault settings are deliberately absent: the lite environment runs the\n# default DB-backed secrets manager; Vault specs live in the K8s CI job.\n\n# Quickstart mode: single container with internal Postgres and an embedded\n# Kind cluster (via the mounted docker socket) for the MCP orchestrator.\nARCHESTRA_QUICKSTART=true\n# No spec in this environment exercises the skill sandbox, so skip the Dagger\n# engine boot — this also lets CI run the -slim image (no dagger-engine.tar).\nARCHESTRA_CODE_RUNTIME_ENABLED=false\n\nENABLE_E2E_TEST_ENDPOINTS=true\nENABLE_TEST_MCP_SERVER=true\nARCHESTRA_ANALYTICS=disabled\n\n# LLM provider base URLs -> WireMock sidecar (per-provider path prefixes)\nARCHESTRA_OPENAI_BASE_URL=http://e2e-tests-wiremock:8080/openai/v1\nARCHESTRA_ANTHROPIC_BASE_URL=http://e2e-tests-wiremock:8080/anthropic\nARCHESTRA_GEMINI_BASE_URL=http://e2e-tests-wiremock:8080/gemini\nARCHESTRA_COHERE_BASE_URL=http://e2e-tests-wiremock:8080/cohere\nARCHESTRA_CEREBRAS_BASE_URL=http://e2e-tests-wiremock:8080/cerebras/v1\nARCHESTRA_VLLM_BASE_URL=http://e2e-tests-wiremock:8080/vllm/v1\nARCHESTRA_OLLAMA_BASE_URL=http://e2e-tests-wiremock:8080/ollama/v1\nARCHESTRA_GROQ_BASE_URL=http://e2e-tests-wiremock:8080/groq/v1\nARCHESTRA_XAI_BASE_URL=http://e2e-tests-wiremock:8080/xai/v1\nARCHESTRA_OPENROUTER_BASE_URL=http://e2e-tests-wiremock:8080/openrouter/api/v1\nARCHESTRA_ZHIPUAI_BASE_URL=http://e2e-tests-wiremock:8080/zhipuai/v4\nARCHESTRA_MISTRAL_BASE_URL=http://e2e-tests-wiremock:8080/mistral/v1\nARCHESTRA_BEDROCK_BASE_URL=http://e2e-tests-wiremock:8080/bedrock\nARCHESTRA_PERPLEXITY_BASE_URL=http://e2e-tests-wiremock:8080/perplexity/v1\nARCHESTRA_DEEPSEEK_BASE_URL=http://e2e-tests-wiremock:8080/deepseek\nARCHESTRA_GITHUB_COPILOT_BASE_URL=http://e2e-tests-wiremock:8080/github-copilot\nARCHESTRA_GITHUB_COPILOT_TOKEN_EXCHANGE_URL=http://e2e-tests-wiremock:8080/github/copilot_internal/v2/token\nARCHESTRA_MICROSOFT_365_COPILOT_BASE_URL=http://e2e-tests-wiremock:8080/microsoft-365-copilot\nARCHESTRA_MICROSOFT_365_COPILOT_AUTH_BASE_URL=http://e2e-tests-wiremock:8080/microsoft-login\nARCHESTRA_MICROSOFT_365_COPILOT_CLIENT_ID=e2e-test-client-id\nARCHESTRA_MINIMAX_BASE_URL=http://e2e-tests-wiremock:8080/minimax/v1\nARCHESTRA_AZURE_OPENAI_BASE_URL=http://e2e-tests-wiremock:8080/azure/openai/deployments/test-deployment\nARCHESTRA_AZURE_OPENAI_API_VERSION=2024-02-01\n\n# Chat API keys (chat routes require a key to be present)\nARCHESTRA_CHAT_OPENAI_API_KEY=test-key\nARCHESTRA_CHAT_ANTHROPIC_API_KEY=test-key\nARCHESTRA_CHAT_GEMINI_API_KEY=test-key\nARCHESTRA_CHAT_COHERE_API_KEY=test-key\nARCHESTRA_CHAT_CEREBRAS_API_KEY=test-key\nARCHESTRA_CHAT_VLLM_API_KEY=test-key\nARCHESTRA_CHAT_OLLAMA_API_KEY=test-key\nARCHESTRA_CHAT_GROQ_API_KEY=test-key\nARCHESTRA_CHAT_XAI_API_KEY=test-key\nARCHESTRA_CHAT_OPENROUTER_API_KEY=test-key\nARCHESTRA_CHAT_ZHIPUAI_API_KEY=test-key\nARCHESTRA_CHAT_MISTRAL_API_KEY=test-key\nARCHESTRA_CHAT_BEDROCK_API_KEY=test-key\nARCHESTRA_CHAT_PERPLEXITY_API_KEY=test-key\nARCHESTRA_CHAT_DEEPSEEK_API_KEY=test-key\nARCHESTRA_CHAT_GITHUB_COPILOT_API_KEY=test-key\nARCHESTRA_CHAT_MINIMAX_API_KEY=test-key\nARCHESTRA_CHAT_AZURE_OPENAI_API_KEY=test-key\n\nARCHESTRA_METRICS_SECRET=foo-bar\nARCHESTRA_FRONTEND_URL=http://localhost:3000\nARCHESTRA_AUTH_SECRET=e2e-auth-secret-must-be-32-chars-long\n\n# Enterprise license flags (required for the .ee specs)\nARCHESTRA_ENTERPRISE_LICENSE_ACTIVATED=true\nARCHESTRA_ENTERPRISE_LICENSE_FULL_WHITE_LABELING=true\nARCHESTRA_ENTERPRISE_LICENSE_KNOWLEDGE_BASE_ACTIVATED=true\n\n# Beta feature gates exercised by the suite\nARCHESTRA_KNOWLEDGE_BASE_AUTO_SYNC_PERMISSIONS_ENABLED=true\n\nARCHESTRA_AUTH_RATE_LIMIT_DISABLED=true\n\n# Keycloak sidecar as trusted origin (OIDC discovery URL validation):\n# the docker network name the backend resolves, and the host-published URL\n# that appears in OIDC discovery documents.\nARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS=http://e2e-tests-keycloak:8080,http://localhost:30081\n"}, "06deee5c509ab02fbcc2398d154eb317e66d747b": {"t": 1785846889.546342, "n": 4259, "h": "8aa3e475f0569f36", "c": "# Platform container environment for the lite e2e harness (docker --env-file).\n#\n# Mirrors `archestra.env` in .github/values-ci.yaml (the Kind+Helm CI job).\n# Keep the two in sync: chat.spec.ts silently skips providers whose base URL\n# is not configured, so a provider added there but not here loses coverage\n# without failing anything.\n#\n# Vault settings are deliberately absent: the lite environment runs the\n# default DB-backed secrets manager; Vault specs live in the K8s CI job.\n\n# Quickstart mode: single container with internal Postgres and an embedded\n# Kind cluster (via the mounted docker socket) for the MCP orchestrator.\nARCHESTRA_QUICKSTART=true\n# No spec in this environment exercises the skill sandbox, so skip the Dagger\n# engine boot — this also lets CI run the -slim image (no dagger-engine.tar).\nARCHESTRA_CODE_RUNTIME_ENABLED=false\n\nENABLE_E2E_TEST_ENDPOINTS=true\nENABLE_TEST_MCP_SERVER=true\nARCHESTRA_ANALYTICS=disabled\n\n# LLM provider base URLs -> WireMock sidecar (per-provider path prefixes)\nARCHESTRA_OPENAI_BASE_URL=http://e2e-tests-wiremock:8080/openai/v1\nARCHESTRA_ANTHROPIC_BASE_URL=http://e2e-tests-wiremock:8080/anthropic\nARCHESTRA_GEMINI_BASE_URL=http://e2e-tests-wiremock:8080/gemini\nARCHESTRA_COHERE_BASE_URL=http://e2e-tests-wiremock:8080/cohere\nARCHESTRA_CEREBRAS_BASE_URL=http://e2e-tests-wiremock:8080/cerebras/v1\nARCHESTRA_VLLM_BASE_URL=http://e2e-tests-wiremock:8080/vllm/v1\nARCHESTRA_OLLAMA_BASE_URL=http://e2e-tests-wiremock:8080/ollama/v1\nARCHESTRA_GROQ_BASE_URL=http://e2e-tests-wiremock:8080/groq/v1\nARCHESTRA_XAI_BASE_URL=http://e2e-tests-wiremock:8080/xai/v1\nARCHESTRA_OPENROUTER_BASE_URL=http://e2e-tests-wiremock:8080/openrouter/api/v1\nARCHESTRA_ZHIPUAI_BASE_URL=http://e2e-tests-wiremock:8080/zhipuai/v4\nARCHESTRA_MISTRAL_BASE_URL=http://e2e-tests-wiremock:8080/mistral/v1\nARCHESTRA_BEDROCK_BASE_URL=http://e2e-tests-wiremock:8080/bedrock\nARCHESTRA_PERPLEXITY_BASE_URL=http://e2e-tests-wiremock:8080/perplexity/v1\nARCHESTRA_DEEPSEEK_BASE_URL=http://e2e-tests-wiremock:8080/deepseek\nARCHESTRA_GITHUB_COPILOT_BASE_URL=http://e2e-tests-wiremock:8080/github-copilot\nARCHESTRA_GITHUB_COPILOT_TOKEN_EXCHANGE_URL=http://e2e-tests-wiremock:8080/github/copilot_internal/v2/token\nARCHESTRA_MICROSOFT_365_COPILOT_BASE_URL=http://e2e-tests-wiremock:8080/microsoft-365-copilot\nARCHESTRA_MICROSOFT_365_COPILOT_AUTH_BASE_URL=http://e2e-tests-wiremock:8080/microsoft-login\nARCHESTRA_MICROSOFT_365_COPILOT_CLIENT_ID=e2e-test-client-id\nARCHESTRA_MINIMAX_BASE_URL=http://e2e-tests-wiremock:8080/minimax/v1\nARCHESTRA_AZURE_OPENAI_BASE_URL=http://e2e-tests-wiremock:8080/azure/openai/deployments/test-deployment\nARCHESTRA_AZURE_OPENAI_API_VERSION=2024-02-01\n\n# Chat API keys (chat routes require a key to be present)\nARCHESTRA_CHAT_OPENAI_API_KEY=test-key\nARCHESTRA_CHAT_ANTHROPIC_API_KEY=test-key\nARCHESTRA_CHAT_GEMINI_API_KEY=test-key\nARCHESTRA_CHAT_COHERE_API_KEY=test-key\nARCHESTRA_CHAT_CEREBRAS_API_KEY=test-key\nARCHESTRA_CHAT_VLLM_API_KEY=test-key\nARCHESTRA_CHAT_OLLAMA_API_KEY=test-key\nARCHESTRA_CHAT_GROQ_API_KEY=test-key\nARCHESTRA_CHAT_XAI_API_KEY=test-key\nARCHESTRA_CHAT_OPENROUTER_API_KEY=test-key\nARCHESTRA_CHAT_ZHIPUAI_API_KEY=test-key\nARCHESTRA_CHAT_MISTRAL_API_KEY=test-key\nARCHESTRA_CHAT_BEDROCK_API_KEY=test-key\nARCHESTRA_CHAT_PERPLEXITY_API_KEY=test-key\nARCHESTRA_CHAT_DEEPSEEK_API_KEY=test-key\nARCHESTRA_CHAT_GITHUB_COPILOT_API_KEY=test-key\nARCHESTRA_CHAT_MINIMAX_API_KEY=test-key\nARCHESTRA_CHAT_AZURE_OPENAI_API_KEY=test-key\n\nARCHESTRA_METRICS_SECRET=foo-bar\nARCHESTRA_FRONTEND_URL=http://localhost:3000\nARCHESTRA_AUTH_SECRET=e2e-auth-secret-must-be-32-chars-long\n\n# Enterprise license flags (required for the .ee specs)\nARCHESTRA_ENTERPRISE_LICENSE_ACTIVATED=true\nARCHESTRA_ENTERPRISE_LICENSE_FULL_WHITE_LABELING=true\nARCHESTRA_ENTERPRISE_LICENSE_KNOWLEDGE_BASE_ACTIVATED=true\n\n# Beta feature gates exercised by the suite\nARCHESTRA_KNOWLEDGE_BASE_AUTO_SYNC_PERMISSIONS_ENABLED=true\n\nARCHESTRA_AUTH_RATE_LIMIT_DISABLED=true\n\n# Keycloak sidecar as trusted origin (OIDC discovery URL validation):\n# the docker network name the backend resolves, and the host-published URL\n# that appears in OIDC discovery documents.\nARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS=http://e2e-tests-keycloak:8080,http://localhost:30081\n"}, "596a18aa85019433ade3bef2b3745e75ccea5ddc": {"t": 1786068195.0907803, "n": 4259, "h": "8aa3e475f0569f36", "c": "# Platform container environment for the lite e2e harness (docker --env-file).\n#\n# Mirrors `archestra.env` in .github/values-ci.yaml (the Kind+Helm CI job).\n# Keep the two in sync: chat.spec.ts silently skips providers whose base URL\n# is not configured, so a provider added there but not here loses coverage\n# without failing anything.\n#\n# Vault settings are deliberately absent: the lite environment runs the\n# default DB-backed secrets manager; Vault specs live in the K8s CI job.\n\n# Quickstart mode: single container with internal Postgres and an embedded\n# Kind cluster (via the mounted docker socket) for the MCP orchestrator.\nARCHESTRA_QUICKSTART=true\n# No spec in this environment exercises the skill sandbox, so skip the Dagger\n# engine boot — this also lets CI run the -slim image (no dagger-engine.tar).\nARCHESTRA_CODE_RUNTIME_ENABLED=false\n\nENABLE_E2E_TEST_ENDPOINTS=true\nENABLE_TEST_MCP_SERVER=true\nARCHESTRA_ANALYTICS=disabled\n\n# LLM provider base URLs -> WireMock sidecar (per-provider path prefixes)\nARCHESTRA_OPENAI_BASE_URL=http://e2e-tests-wiremock:8080/openai/v1\nARCHESTRA_ANTHROPIC_BASE_URL=http://e2e-tests-wiremock:8080/anthropic\nARCHESTRA_GEMINI_BASE_URL=http://e2e-tests-wiremock:8080/gemini\nARCHESTRA_COHERE_BASE_URL=http://e2e-tests-wiremock:8080/cohere\nARCHESTRA_CEREBRAS_BASE_URL=http://e2e-tests-wiremock:8080/cerebras/v1\nARCHESTRA_VLLM_BASE_URL=http://e2e-tests-wiremock:8080/vllm/v1\nARCHESTRA_OLLAMA_BASE_URL=http://e2e-tests-wiremock:8080/ollama/v1\nARCHESTRA_GROQ_BASE_URL=http://e2e-tests-wiremock:8080/groq/v1\nARCHESTRA_XAI_BASE_URL=http://e2e-tests-wiremock:8080/xai/v1\nARCHESTRA_OPENROUTER_BASE_URL=http://e2e-tests-wiremock:8080/openrouter/api/v1\nARCHESTRA_ZHIPUAI_BASE_URL=http://e2e-tests-wiremock:8080/zhipuai/v4\nARCHESTRA_MISTRAL_BASE_URL=http://e2e-tests-wiremock:8080/mistral/v1\nARCHESTRA_BEDROCK_BASE_URL=http://e2e-tests-wiremock:8080/bedrock\nARCHESTRA_PERPLEXITY_BASE_URL=http://e2e-tests-wiremock:8080/perplexity/v1\nARCHESTRA_DEEPSEEK_BASE_URL=http://e2e-tests-wiremock:8080/deepseek\nARCHESTRA_GITHUB_COPILOT_BASE_URL=http://e2e-tests-wiremock:8080/github-copilot\nARCHESTRA_GITHUB_COPILOT_TOKEN_EXCHANGE_URL=http://e2e-tests-wiremock:8080/github/copilot_internal/v2/token\nARCHESTRA_MICROSOFT_365_COPILOT_BASE_URL=http://e2e-tests-wiremock:8080/microsoft-365-copilot\nARCHESTRA_MICROSOFT_365_COPILOT_AUTH_BASE_URL=http://e2e-tests-wiremock:8080/microsoft-login\nARCHESTRA_MICROSOFT_365_COPILOT_CLIENT_ID=e2e-test-client-id\nARCHESTRA_MINIMAX_BASE_URL=http://e2e-tests-wiremock:8080/minimax/v1\nARCHESTRA_AZURE_OPENAI_BASE_URL=http://e2e-tests-wiremock:8080/azure/openai/deployments/test-deployment\nARCHESTRA_AZURE_OPENAI_API_VERSION=2024-02-01\n\n# Chat API keys (chat routes require a key to be present)\nARCHESTRA_CHAT_OPENAI_API_KEY=test-key\nARCHESTRA_CHAT_ANTHROPIC_API_KEY=test-key\nARCHESTRA_CHAT_GEMINI_API_KEY=test-key\nARCHESTRA_CHAT_COHERE_API_KEY=test-key\nARCHESTRA_CHAT_CEREBRAS_API_KEY=test-key\nARCHESTRA_CHAT_VLLM_API_KEY=test-key\nARCHESTRA_CHAT_OLLAMA_API_KEY=test-key\nARCHESTRA_CHAT_GROQ_API_KEY=test-key\nARCHESTRA_CHAT_XAI_API_KEY=test-key\nARCHESTRA_CHAT_OPENROUTER_API_KEY=test-key\nARCHESTRA_CHAT_ZHIPUAI_API_KEY=test-key\nARCHESTRA_CHAT_MISTRAL_API_KEY=test-key\nARCHESTRA_CHAT_BEDROCK_API_KEY=test-key\nARCHESTRA_CHAT_PERPLEXITY_API_KEY=test-key\nARCHESTRA_CHAT_DEEPSEEK_API_KEY=test-key\nARCHESTRA_CHAT_GITHUB_COPILOT_API_KEY=test-key\nARCHESTRA_CHAT_MINIMAX_API_KEY=test-key\nARCHESTRA_CHAT_AZURE_OPENAI_API_KEY=test-key\n\nARCHESTRA_METRICS_SECRET=foo-bar\nARCHESTRA_FRONTEND_URL=http://localhost:3000\nARCHESTRA_AUTH_SECRET=e2e-auth-secret-must-be-32-chars-long\n\n# Enterprise license flags (required for the .ee specs)\nARCHESTRA_ENTERPRISE_LICENSE_ACTIVATED=true\nARCHESTRA_ENTERPRISE_LICENSE_FULL_WHITE_LABELING=true\nARCHESTRA_ENTERPRISE_LICENSE_KNOWLEDGE_BASE_ACTIVATED=true\n\n# Beta feature gates exercised by the suite\nARCHESTRA_KNOWLEDGE_BASE_AUTO_SYNC_PERMISSIONS_ENABLED=true\n\nARCHESTRA_AUTH_RATE_LIMIT_DISABLED=true\n\n# Keycloak sidecar as trusted origin (OIDC discovery URL validation):\n# the docker network name the backend resolves, and the host-published URL\n# that appears in OIDC discovery documents.\nARCHESTRA_AUTH_ADDITIONAL_TRUSTED_ORIGINS=http://e2e-tests-keycloak:8080,http://localhost:30081\n"}}, "mcp-catalog/data/mcp-evaluations/gwbischof__outsource-mcp.json": {"f41ed6e2f62c4034a03f4847a14f365244631687": {"t": 1785674623.9191632, "n": 17261, "h": "16a290469b752016", "c": "{\n \"name\": \"gwbischof__outsource-mcp\",\n \"display_name\": \"outsource-mcp\",\n \"description\": \"Give your AI assistant its own AI assistants.\",\n \"author\": {\n \"name\": \"gwbischof\"\n },\n \"server\": {\n \"command\": \"uvx\",\n \"args\": [\"--from\", \"git+https://github.com/gwbischof/outsource-mcp.git\", \"outsource-mcp\"],\n \"env\": {\n \"OPENAI_API_KEY\": \"${user_config.openai_api_key}\",\n \"ANTHROPIC_API_KEY\": \"${user_config.anthropic_api_key}\",\n \"GOOGLE_API_KEY\": \"${user_config.google_api_key}\",\n \"GROQ_API_KEY\": \"${user_config.groq_api_key}\",\n \"DEEPSEEK_API_KEY\": \"${user_config.deepseek_api_key}\",\n \"XAI_API_KEY\": \"${user_config.xai_api_key}\",\n \"PERPLEXITY_API_KEY\": \"${user_config.perplexity_api_key}\",\n \"COHERE_API_KEY\": \"${user_config.cohere_api_key}\",\n \"FIREWORKS_API_KEY\": \"${user_config.fireworks_api_key}\",\n \"HUGGINGFACE_API_KEY\": \"${user_config.huggingface_api_key}\",\n \"MISTRAL_API_KEY\": \"${user_config.mistral_api_key}\",\n \"NVIDIA_API_KEY\": \"${user_config.nvidia_api_key}\",\n \"OLLAMA_HOST\": \"${user_config.ollama_host}\",\n \"OPENROUTER_API_KEY\": \"${user_config.openrouter_api_key}\",\n \"TOGETHER_API_KEY\": \"${user_config.together_api_key}\",\n \"CEREBRAS_API_KEY\": \"${user_config.cerebras_api_key}\",\n \"DEEPINFRA_API_KEY\": \"${user_config.deepinfra_api_key}\",\n \"SAMBANOVA_API_KEY\": \"${user_config.sambanova_api_key}\",\n \"IBM_WATSONX_API_KEY\": \"${user_config.ibm_watsonx_api_key}\",\n \"LLAMA_API_KEY\": \"${user_config.llama_api_key}\"\n },\n \"type\": \"local\"\n },\n \"tools\": [],\n \"prompts\": [],\n \"keywords\": [],\n \"user_config\": {\n \"openai_api_key\": {\n \"type\": \"string\",\n \"title\": \"OpenAI API Key\",\n \"description\": \"Your API key for OpenAI\",\n \"sensitive\": true,\n \"required\": false\n },\n \"anthropic_api_key\": {\n \"type\": \"string\",\n \"title\": \"Anthropic API Key\",\n \"description\": \"Your API key for Anthropic\",\n \"sensitive\": true,\n \"required\": false\n },\n \"google_api_key\": {\n \"type\": \"string\",\n \"title\": \"Google API Key\",\n \"description\": \"Your API key for Google\",\n \"sensitive\": true,\n \"required\": false\n },\n \"groq_api_key\": {\n \"type\": \"string\",\n \"title\": \"Groq API Key\",\n \"description\": \"Your API key for Groq\",\n \"sensitive\": true,\n \"required\": false\n },\n \"deepseek_api_key\": {\n \"type\": \"string\",\n \"title\": \"DeepSeek API Key\",\n \"description\": \"Your API key for DeepSeek\",\n \"sensitive\": true,\n \"required\": false\n },\n \"xai_api_key\": {\n \"type\": \"string\",\n \"title\": \"xAI API Key\",\n \"description\": \"Your API key for xAI\",\n \"sensitive\": true,\n \"required\": false\n },\n \"perplexity_api_key\": {\n \"type\": \"string\",\n \"title\": \"Perplexity API Key\",\n \"description\": \"Your API key for Perplexity\",\n \"sensitive\": true,\n \"required\": false\n },\n \"cohere_api_key\": {\n \"type\": \"string\",\n \"title\": \"Cohere API Key\",\n \"description\": \"Your API key for Cohere\",\n \"sensitive\": true,\n \"required\": false\n },\n \"fireworks_api_key\": {\n \"type\": \"string\",\n \"title\": \"Fireworks AI API Key\",\n \"description\": \"Your API key for Fireworks AI\",\n \"sensitive\": true,\n \"required\": false\n },\n \"huggingface_api_key\": {\n \"type\": \"string\",\n \"title\": \"HuggingFace API Key\",\n \"description\": \"Your API key for HuggingFace\",\n \"sensitive\": true,\n \"required\": false\n },\n \"mistral_api_key\": {\n \"type\": \"string\",\n \"title\": \"Mistral AI API Key\",\n \"description\": \"Your API key for Mistral AI\",\n \"sensitive\": true,\n \"required\": false\n },\n \"nvidia_api_key\": {\n \"type\": \"string\",\n \"title\": \"NVIDIA API Key\",\n \"description\": \"Your API key for NVIDIA\",\n \"sensitive\": true,\n \"required\": false\n },\n \"ollama_host\": {\n \"type\": \"string\",\n \"title\": \"Ollama Host\",\n \"description\": \"The host URL for Ollama\",\n \"default\": \"http://localhost:11434\",\n \"sensitive\": false,\n \"required\": false\n },\n \"openrouter_api_key\": {\n \"type\": \"string\",\n \"title\": \"OpenRouter API Key\",\n \"description\": \"Your API key for OpenRouter\",\n \"sensitive\": true,\n \"required\": false\n },\n \"together_api_key\": {\n \"type\": \"string\",\n \"title\": \"Together AI API Key\",\n \"description\": \"Your API key for Together AI\",\n \"sensitive\": true,\n \"required\": false\n },\n \"cerebras_api_key\": {\n \"type\": \"string\",\n \"title\": \"Cerebras API Key\",\n \"description\": \"Your API key for Cerebras\",\n \"sensitive\": true,\n \"required\": false\n },\n \"deepinfra_api_key\": {\n \"type\": \"string\",\n \"title\": \"DeepInfra API Key\",\n \"description\": \"Your API key for DeepInfra\",\n \"sensitive\": true,\n \"required\": false\n },\n \"sambanova_api_key\": {\n \"type\": \"string\",\n \"title\": \"SambaNova API Key\",\n \"description\": \"Your API key for SambaNova\",\n \"sensitive\": true,\n \"required\": false\n },\n \"ibm_watsonx_api_key\": {\n \"type\": \"string\",\n \"title\": \"IBM WatsonX API Key\",\n \"description\": \"Your API key for IBM WatsonX\",\n \"sensitive\": true,\n \"required\": false\n },\n \"llama_api_key\": {\n \"type\": \"string\",\n \"title\": \"Meta Llama API Key\",\n \"description\": \"Your API key for Meta Llama\",\n \"sensitive\": true,\n \"required\": false\n }\n },\n \"readme\": \"# Outsource MCP\\n\\nAn MCP (Model Context Protocol) server that enables AI applications to outsource tasks to various model providers through a unified interface.\\n\\n<img width=\\\"1154\\\" alt=\\\"image\\\" src=\\\"https://github.com/user-attachments/assets/cd364a7c-eae5-4c58-bc1f-fdeea6cb8434\\\" />\\n\\n<img width=\\\"1103\\\" alt=\\\"image\\\" src=\\\"https://github.com/user-attachments/assets/55924981-83e9-4811-9f51-b049595b7505\\\" />\\n\\n\\nCompatible with any AI tool that supports the Model Context Protocol, including Claude Desktop, Cline, and other MCP-enabled applications.\\nBuilt with [FastMCP](https://github.com/mcp/fastmcp) for the MCP server implementation and [Agno](https://github.com/agno-agi/agno) for AI agent capabilities.\\n\\n## Features\\n\\n- 🤖 **Multi-Provider Support**: Access 20+ AI providers through a single interface\\n- 📝 **Text Generation**: Generate text using models from OpenAI, Anthropic, Google, and more\\n- 🎨 **Image Generation**: Create images using DALL-E 3 and DALL-E 2\\n- 🔧 **Simple API**: Consistent interface with just three parameters: provider, model, and prompt\\n- 🔑 **Flexible Authentication**: Only configure API keys for the providers you use\\n\\n## Configuration\\n\\nAdd the following configuration to your MCP client. Consult your MCP client's documentation for specific configuration details.\\n\\n```json\\n{\\n \\\"mcpServers\\\": {\\n \\\"outsource-mcp\\\": {\\n \\\"command\\\": \\\"uvx\\\",\\n \\\"args\\\": [\\\"--from\\\", \\\"git+https://github.com/gwbischof/outsource-mcp.git\\\", \\\"outsource-mcp\\\"],\\n \\\"env\\\": {\\n \\\"OPENAI_API_KEY\\\": \\\"your-openai-key\\\",\\n \\\"ANTHROPIC_API_KEY\\\": \\\"your-anthropic-key\\\",\\n \\\"GOOGLE_API_KEY\\\": \\\"your-google-key\\\",\\n \\\"GROQ_API_KEY\\\": \\\"your-groq-key\\\",\\n \\\"DEEPSEEK_API_KEY\\\": \\\"your-deepseek-key\\\",\\n \\\"XAI_API_KEY\\\": \\\"your-xai-key\\\",\\n \\\"PERPLEXITY_API_KEY\\\": \\\"your-perplexity-key\\\",\\n \\\"COHERE_API_KEY\\\": \\\"your-cohere-key\\\",\\n \\\"FIREWORKS_API_KEY\\\": \\\"your-fireworks-key\\\",\\n \\\"HUGGINGFACE_API_KEY\\\": \\\"your-huggingface-key\\\",\\n \\\"MISTRAL_API_KEY\\\": \\\"your-mistral-key\\\",\\n \\\"NVIDIA_API_KEY\\\": \\\"your-nvidia-key\\\",\\n \\\"OLLAMA_HOST\\\": \\\"http://localhost:11434\\\",\\n \\\"OPENROUTER_API_KEY\\\": \\\"your-openrouter-key\\\",\\n \\\"TOGETHER_API_KEY\\\": \\\"your-together-key\\\",\\n \\\"CEREBRAS_API_KEY\\\": \\\"your-cerebras-key\\\",\\n \\\"DEEPINFRA_API_KEY\\\": \\\"your-deepinfra-key\\\",\\n \\\"SAMBANOVA_API_KEY\\\": \\\"your-sambanova-key\\\"\\n }\\n }\\n }\\n}\\n```\\n\\nNote: The environment variables are optional. Only include the API keys for the providers you want to use.\\n\\n## Quick Start\\n\\nOnce installed and configured, you can use the tools in your MCP client:\\n\\n1. **Generate text**: Use the `outsource_text` tool with provider \\\"openai\\\", model \\\"gpt-4o-mini\\\", and prompt \\\"Write a haiku about coding\\\"\\n2. **Generate images**: Use the `outsource_image` tool with provider \\\"openai\\\", model \\\"dall-e-3\\\", and prompt \\\"A futuristic city skyline at sunset\\\"\\n\\n## Tools\\n\\n### outsource_text\\nCreates an Agno agent with a specified provider and model to generate text responses. \\n\\n**Arguments:**\\n- `provider`: The provider name (e.g., \\\"openai\\\", \\\"anthropic\\\", \\\"google\\\", \\\"groq\\\", etc.)\\n- `model`: The model name (e.g., \\\"gpt-4o\\\", \\\"claude-3-5-sonnet-20241022\\\", \\\"gemini-2.0-flash-exp\\\")\\n- `prompt`: The text prompt to send to the model\\n\\n### outsource_image\\nGenerates images using AI models.\\n\\n**Arguments:**\\n- `provider`: The provider name (currently only \\\"openai\\\" is supported)\\n- `model`: The model name (\\\"dall-e-3\\\" or \\\"dall-e-2\\\")\\n- `prompt`: The image generation prompt\\n\\nReturns the URL of the generated image.\\n\\n> **Note**: Image generation is currently only supported by OpenAI models (DALL-E 2 and DALL-E 3). Other providers only support text generation.\\n\\n## Supported Providers\\n\\nThe following providers are supported. Use the provider name (in parentheses) as the `provider` argument:\\n\\n### Core Providers\\n- **OpenAI** (`openai`) - GPT-4, GPT-3.5, DALL-E, etc. | [Models](https://platform.openai.com/docs/models)\\n- **Anthropic** (`anthropic`) - Claude 3.5, Claude 3, etc. | [Models](https://docs.anthropic.com/en/docs/about-claude/models/overview)\\n- **Google** (`google`) - Gemini Pro, Gemini Flash, etc. | [Models](https://ai.google.dev/models)\\n- **Groq** (`groq`) - Llama 3, Mixtral, etc. | [Models](https://console.groq.com/docs/models)\\n- **DeepSeek** (`deepseek`) - DeepSeek Chat & Coder | [Models](https://api-docs.deepseek.com/api/list-models)\\n- **xAI** (`xai`) - Grok models | [Models](https://docs.x.ai/docs/models)\\n- **Perplexity** (`perplexity`) - Sonar models | [Models](https://docs.perplexity.ai/guides/model-cards)\\n\\n### Additional Providers\\n- **Cohere** (`cohere`) - Command models | [Models](https://docs.cohere.com/v2/docs/models)\\n- **Mistral AI** (`mistral`) - Mistral Large, Medium, Small | [Models](https://docs.mistral.ai/getting-started/models/models_overview/)\\n- **NVIDIA** (`nvidia`) - Various models | [Models](https://build.nvidia.com/models)\\n- **HuggingFace** (`huggingface`) - Open source models | [Models](https://huggingface.co/models)\\n- **Ollama** (`ollama`) - Local models | [Models](https://ollama.com/library)\\n- **Fireworks AI** (`fireworks`) - Fast inference | [Models](https://fireworks.ai/models?view=list)\\n- **OpenRouter** (`openrouter`) - Multi-provider access | [Models](https://openrouter.ai/docs/overview/models)\\n- **Together AI** (`together`) - Open source models | [Models](https://docs.together.ai/docs/serverless-models)\\n- **Cerebras** (`cerebras`) - Fast inference | [Models](https://cerebras.ai/models)\\n- **DeepInfra** (`deepinfra`) - Optimized models | [Models](https://deepinfra.com/docs/models)\\n- **SambaNova** (`sambanova`) - Enterprise models | [Models](https://docs.sambanova.ai/cloud/docs/get-started/supported-models)\\n\\n### Enterprise Providers\\n- **AWS Bedrock** (`aws` or `bedrock`) - AWS-hosted models | [Models](https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html)\\n- **Azure AI** (`azure`) - Azure-hosted models | [Models](https://learn.microsoft.com/en-us/azure/ai-foundry/concepts/foundry-models-overview)\\n- **IBM WatsonX** (`ibm` or `watsonx`) - IBM models | [Models](https://www.ibm.com/docs/en/software-hub/5.1.x?topic=install-foundation-models)\\n- **LiteLLM** (`litellm`) - Universal interface | [Models](https://docs.litellm.ai/docs/providers)\\n- **Vercel v0** (`vercel` or `v0`) - Vercel AI | [Models](https://sdk.vercel.ai/docs/introduction)\\n- **Meta Llama** (`meta`) - Direct Meta access | [Models](https://www.llama.com/get-started/)\\n\\n### Environment Variables\\n\\nEach provider requires its corresponding API key:\\n\\n| Provider | Environment Variable | Example |\\n|----------|---------------------|---------|\\n| OpenAI | `OPENAI_API_KEY` | sk-... |\\n| Anthropic | `ANTHROPIC_API_KEY` | sk-ant-... |\\n| Google | `GOOGLE_API_KEY` | AIza... |\\n| Groq | `GROQ_API_KEY` | gsk_... |\\n| DeepSeek | `DEEPSEEK_API_KEY` | sk-... |\\n| xAI | `XAI_API_KEY` | xai-... |\\n| Perplexity | `PERPLEXITY_API_KEY` | pplx-... |\\n| Cohere | `COHERE_API_KEY` | ... |\\n| Fireworks | `FIREWORKS_API_KEY` | ... |\\n| HuggingFace | `HUGGINGFACE_API_KEY` | hf_... |\\n| Mistral | `MISTRAL_API_KEY` | ... |\\n| NVIDIA | `NVIDIA_API_KEY` | nvapi-... |\\n| Ollama | `OLLAMA_HOST` | http://localhost:11434 |\\n| OpenRouter | `OPENROUTER_API_KEY` | ... |\\n| Together | `TOGETHER_API_KEY` | ... |\\n| Cerebras | `CEREBRAS_API_KEY` | ... |\\n| DeepInfra | `DEEPINFRA_API_KEY` | ... |\\n| SambaNova | `SAMBANOVA_API_KEY` | ... |\\n| AWS Bedrock | AWS credentials | Via AWS CLI/SDK |\\n| Azure AI | Azure credentials | Via Azure CLI/SDK |\\n| IBM WatsonX | `IBM_WATSONX_API_KEY` | ... |\\n| Meta Llama | `LLAMA_API_KEY` | ... |\\n\\n**Note**: Only configure the API keys for providers you plan to use.\\n\\n## Examples\\n\\n### Text Generation\\n```\\n# Using OpenAI\\nprovider: openai\\nmodel: gpt-4o-mini\\nprompt: Write a haiku about coding\\n\\n# Using Anthropic\\nprovider: anthropic\\nmodel: claude-3-5-sonnet-20241022\\nprompt: Explain quantum computing in simple terms\\n\\n# Using Google\\nprovider: google\\nmodel: gemini-2.0-flash-exp\\nprompt: Create a recipe for chocolate chip cookies\\n```\\n\\n### Image Generation\\n```\\n# Using DALL-E 3\\nprovider: openai\\nmodel: dall-e-3\\nprompt: A serene Japanese garden with cherry blossoms\\n\\n# Using DALL-E 2\\nprovider: openai\\nmodel: dall-e-2\\nprompt: A futuristic cityscape at sunset\\n```\\n\\n## Development\\n\\n### Prerequisites\\n\\n- Python 3.11 or higher\\n- [uv](https://github.com/astral-sh/uv) package manager\\n\\n### Setup\\n\\n```bash\\ngit clone https://github.com/gwbischof/outsource-mcp.git\\ncd outsource-mcp\\nuv sync\\n```\\n\\n### Testing with MCP Inspector\\n\\nThe MCP Inspector allows you to test the server interactively:\\n\\n```bash\\nmcp dev server.py\\n```\\n\\n### Running Tests\\n\\nThe test suite includes integration tests that verify both text and image generation:\\n\\n```bash\\n# Run all tests\\nuv run pytest\\n```\\n\\n**Note:** Integration tests require API keys to be set in your environment.\\n\\n## Troubleshooting\\n\\n### Common Issues\\n\\n1. **\\\"Error: Unknown provider\\\"**\\n - Check that you're using a supported provider name from the list above\\n - Provider names are case-insensitive\\n\\n2. **\\\"Error: OpenAI API error\\\"** \\n - Verify your API key is correctly set in the environment variables\\n - Check that your API key has access to the requested model\\n - Ensure you have sufficient credits/quota\\n\\n3. **\\\"Error: No image was generated\\\"**\\n - This can happen if the image generation request fails\\n - Try a simpler prompt or different model (dall-e-2 vs dall-e-3)\\n\\n4. **Environment variables not working**\\n - Make sure to restart your MCP client after updating the configuration\\n - Verify the configuration file location for your specific MCP client\\n - Check that the environment variables are properly formatted in the configuration\\n\\n## Contributing\\n\\nContributions are welcome! Please feel free to submit a Pull Request.\\n\",\n \"category\": \"AI Tools\",\n \"archestra_config\": {\n \"client_config_permutations\": {\n \"outsource-mcp\": {\n \"command\": \"uvx\",\n \"args\": [\"--from\", \"git+https://github.com/gwbischof/outsource-mcp.git\", \"outsource-mcp\"],\n \"env\": {\n \"OPENAI_API_KEY\": \"your-openai-key\",\n \"ANTHROPIC_API_KEY\": \"your-anthropic-key\",\n \"GOOGLE_API_KEY\": \"your-google-key\",\n \"GROQ_API_KEY\": \"your-groq-key\",\n \"DEEPSEEK_API_KEY\": \"your-deepseek-key\",\n \"XAI_API_KEY\": \"your-xai-key\",\n \"PERPLEXITY_API_KEY\": \"your-perplexity-key\",\n \"COHERE_API_KEY\": \"your-cohere-key\",\n \"FIREWORKS_API_KEY\": \"your-fireworks-key\",\n \"HUGGINGFACE_API_KEY\": \"your-huggingface-key\",\n \"MISTRAL_API_KEY\": \"your-mistral-key\",\n \"NVIDIA_API_KEY\": \"your-nvidia-key\",\n \"OLLAMA_HOST\": \"http://localhost:11434\",\n \"OPENROUTER_API_KEY\": \"your-openrouter-key\",\n \"TOGETHER_API_KEY\": \"your-together-key\",\n \"CEREBRAS_API_KEY\": \"your-cerebras-key\",\n \"DEEPINFRA_API_KEY\": \"your-deepinfra-key\",\n \"SAMBANOVA_API_KEY\": \"your-sambanova-key\"\n }\n }\n },\n \"oauth\": {\n \"provider\": null,\n \"required\": false\n },\n \"works_in_archestra\": false\n },\n \"github_info\": {\n \"owner\": \"gwbischof\",\n \"repo\": \"outsource-mcp\",\n \"url\": \"https://github.com/gwbischof/outsource-mcp\",\n \"name\": \"gwbischof__outsource-mcp\",\n \"path\": null,\n \"stars\": 22,\n \"contributors\": 1,\n \"issues\": 0,\n \"releases\": false,\n \"ci_cd\": false,\n \"latest_commit_hash\": \"70e7b6b4031d1819fad18ee313fb6ec16e126b92\"\n },\n \"programming_language\": \"Python\"\n}\n"}, "06deee5c509ab02fbcc2398d154eb317e66d747b": {"t": 1785846904.0893073, "n": 17261, "h": "16a290469b752016", "c": "{\n \"name\": \"gwbischof__outsource-mcp\",\n \"display_name\": \"outsource-mcp\",\n \"description\": \"Give your AI assistant its own AI assistants.\",\n \"author\": {\n \"name\": \"gwbischof\"\n },\n \"server\": {\n \"command\": \"uvx\",\n \"args\": [\"--from\", \"git+https://github.com/gwbischof/outsource-mcp.git\", \"outsource-mcp\"],\n \"env\": {\n \"OPENAI_API_KEY\": \"${user_config.openai_api_key}\",\n \"ANTHROPIC_API_KEY\": \"${user_config.anthropic_api_key}\",\n \"GOOGLE_API_KEY\": \"${user_config.google_api_key}\",\n \"GROQ_API_KEY\": \"${user_config.groq_api_key}\",\n \"DEEPSEEK_API_KEY\": \"${user_config.deepseek_api_key}\",\n \"XAI_API_KEY\": \"${user_config.xai_api_key}\",\n \"PERPLEXITY_API_KEY\": \"${user_config.perplexity_api_key}\",\n \"COHERE_API_KEY\": \"${user_config.cohere_api_key}\",\n \"FIREWORKS_API_KEY\": \"${user_config.fireworks_api_key}\",\n \"HUGGINGFACE_API_KEY\": \"${user_config.huggingface_api_key}\",\n \"MISTRAL_API_KEY\": \"${user_config.mistral_api_key}\",\n \"NVIDIA_API_KEY\": \"${user_config.nvidia_api_key}\",\n \"OLLAMA_HOST\": \"${user_config.ollama_host}\",\n \"OPENROUTER_API_KEY\": \"${user_config.openrouter_api_key}\",\n \"TOGETHER_API_KEY\": \"${user_config.together_api_key}\",\n \"CEREBRAS_API_KEY\": \"${user_config.cerebras_api_key}\",\n \"DEEPINFRA_API_KEY\": \"${user_config.deepinfra_api_key}\",\n \"SAMBANOVA_API_KEY\": \"${user_config.sambanova_api_key}\",\n \"IBM_WATSONX_API_KEY\": \"${user_config.ibm_watsonx_api_key}\",\n \"LLAMA_API_KEY\": \"${user_config.llama_api_key}\"\n },\n \"type\": \"local\"\n },\n \"tools\": [],\n \"prompts\": [],\n \"keywords\": [],\n \"user_config\": {\n \"openai_api_key\": {\n \"type\": \"string\",\n \"title\": \"OpenAI API Key\",\n \"description\": \"Your API key for OpenAI\",\n \"sensitive\": true,\n \"required\": false\n },\n \"anthropic_api_key\": {\n \"type\": \"string\",\n \"title\": \"Anthropic API Key\",\n \"description\": \"Your API key for Anthropic\",\n \"sensitive\": true,\n \"required\": false\n },\n \"google_api_key\": {\n \"type\": \"string\",\n \"title\": \"Google API Key\",\n \"description\": \"Your API key for Google\",\n \"sensitive\": true,\n \"required\": false\n },\n \"groq_api_key\": {\n \"type\": \"string\",\n \"title\": \"Groq API Key\",\n \"description\": \"Your API key for Groq\",\n \"sensitive\": true,\n \"required\": false\n },\n \"deepseek_api_key\": {\n \"type\": \"string\",\n \"title\": \"DeepSeek API Key\",\n \"description\": \"Your API key for DeepSeek\",\n \"sensitive\": true,\n \"required\": false\n },\n \"xai_api_key\": {\n \"type\": \"string\",\n \"title\": \"xAI API Key\",\n \"description\": \"Your API key for xAI\",\n \"sensitive\": true,\n \"required\": false\n },\n \"perplexity_api_key\": {\n \"type\": \"string\",\n \"title\": \"Perplexity API Key\",\n \"description\": \"Your API key for Perplexity\",\n \"sensitive\": true,\n \"required\": false\n },\n \"cohere_api_key\": {\n \"type\": \"string\",\n \"title\": \"Cohere API Key\",\n \"description\": \"Your API key for Cohere\",\n \"sensitive\": true,\n \"required\": false\n },\n \"fireworks_api_key\": {\n \"type\": \"string\",\n \"title\": \"Fireworks AI API Key\",\n \"description\": \"Your API key for Fireworks AI\",\n \"sensitive\": true,\n \"required\": false\n },\n \"huggingface_api_key\": {\n \"type\": \"string\",\n \"title\": \"HuggingFace API Key\",\n \"description\": \"Your API key for HuggingFace\",\n \"sensitive\": true,\n \"required\": false\n },\n \"mistral_api_key\": {\n \"type\": \"string\",\n \"title\": \"Mistral AI API Key\",\n \"description\": \"Your API key for Mistral AI\",\n \"sensitive\": true,\n \"required\": false\n },\n \"nvidia_api_key\": {\n \"type\": \"string\",\n \"title\": \"NVIDIA API Key\",\n \"description\": \"Your API key for NVIDIA\",\n \"sensitive\": true,\n \"required\": false\n },\n \"ollama_host\": {\n \"type\": \"string\",\n \"title\": \"Ollama Host\",\n \"description\": \"The host URL for Ollama\",\n \"default\": \"http://localhost:11434\",\n \"sensitive\": false,\n \"required\": false\n },\n \"openrouter_api_key\": {\n \"type\": \"string\",\n \"title\": \"OpenRouter API Key\",\n \"description\": \"Your API key for OpenRouter\",\n \"sensitive\": true,\n \"required\": false\n },\n \"together_api_key\": {\n \"type\": \"string\",\n \"title\": \"Together AI API Key\",\n \"description\": \"Your API key for Together AI\",\n \"sensitive\": true,\n \"required\": false\n },\n \"cerebras_api_key\": {\n \"type\": \"string\",\n \"title\": \"Cerebras API Key\",\n \"description\": \"Your API key for Cerebras\",\n \"sensitive\": true,\n \"required\": false\n },\n \"deepinfra_api_key\": {\n \"type\": \"string\",\n \"title\": \"DeepInfra API Key\",\n \"description\": \"Your API key for DeepInfra\",\n \"sensitive\": true,\n \"required\": false\n },\n \"sambanova_api_key\": {\n \"type\": \"string\",\n \"title\": \"SambaNova API Key\",\n \"description\": \"Your API key for SambaNova\",\n \"sensitive\": true,\n \"required\": false\n },\n \"ibm_watsonx_api_key\": {\n \"type\": \"string\",\n \"title\": \"IBM WatsonX API Key\",\n \"description\": \"Your API key for IBM WatsonX\",\n \"sensitive\": true,\n \"required\": false\n },\n \"llama_api_key\": {\n \"type\": \"string\",\n \"title\": \"Meta Llama API Key\",\n \"description\": \"Your API key for Meta Llama\",\n \"sensitive\": true,\n \"required\": false\n }\n },\n \"readme\": \"# Outsource MCP\\n\\nAn MCP (Model Context Protocol) server that enables AI applications to outsource tasks to various model providers through a unified interface.\\n\\n<img width=\\\"1154\\\" alt=\\\"image\\\" src=\\\"https://github.com/user-attachments/assets/cd364a7c-eae5-4c58-bc1f-fdeea6cb8434\\\" />\\n\\n<img width=\\\"1103\\\" alt=\\\"image\\\" src=\\\"https://github.com/user-attachments/assets/55924981-83e9-4811-9f51-b049595b7505\\\" />\\n\\n\\nCompatible with any AI tool that supports the Model Context Protocol, including Claude Desktop, Cline, and other MCP-enabled applications.\\nBuilt with [FastMCP](https://github.com/mcp/fastmcp) for the MCP server implementation and [Agno](https://github.com/agno-agi/agno) for AI agent capabilities.\\n\\n## Features\\n\\n- 🤖 **Multi-Provider Support**: Access 20+ AI providers through a single interface\\n- 📝 **Text Generation**: Generate text using models from OpenAI, Anthropic, Google, and more\\n- 🎨 **Image Generation**: Create images using DALL-E 3 and DALL-E 2\\n- 🔧 **Simple API**: Consistent interface with just three parameters: provider, model, and prompt\\n- 🔑 **Flexible Authentication**: Only configure API keys for the providers you use\\n\\n## Configuration\\n\\nAdd the following configuration to your MCP client. Consult your MCP client's documentation for specific configuration details.\\n\\n```json\\n{\\n \\\"mcpServers\\\": {\\n \\\"outsource-mcp\\\": {\\n \\\"command\\\": \\\"uvx\\\",\\n \\\"args\\\": [\\\"--from\\\", \\\"git+https://github.com/gwbischof/outsource-mcp.git\\\", \\\"outsource-mcp\\\"],\\n \\\"env\\\": {\\n \\\"OPENAI_API_KEY\\\": \\\"your-openai-key\\\",\\n \\\"ANTHROPIC_API_KEY\\\": \\\"your-anthropic-key\\\",\\n \\\"GOOGLE_API_KEY\\\": \\\"your-google-key\\\",\\n \\\"GROQ_API_KEY\\\": \\\"your-groq-key\\\",\\n \\\"DEEPSEEK_API_KEY\\\": \\\"your-deepseek-key\\\",\\n \\\"XAI_API_KEY\\\": \\\"your-xai-key\\\",\\n \\\"PERPLEXITY_API_KEY\\\": \\\"your-perplexity-key\\\",\\n \\\"COHERE_API_KEY\\\": \\\"your-cohere-key\\\",\\n \\\"FIREWORKS_API_KEY\\\": \\\"your-fireworks-key\\\",\\n \\\"HUGGINGFACE_API_KEY\\\": \\\"your-huggingface-key\\\",\\n \\\"MISTRAL_API_KEY\\\": \\\"your-mistral-key\\\",\\n \\\"NVIDIA_API_KEY\\\": \\\"your-nvidia-key\\\",\\n \\\"OLLAMA_HOST\\\": \\\"http://localhost:11434\\\",\\n \\\"OPENROUTER_API_KEY\\\": \\\"your-openrouter-key\\\",\\n \\\"TOGETHER_API_KEY\\\": \\\"your-together-key\\\",\\n \\\"CEREBRAS_API_KEY\\\": \\\"your-cerebras-key\\\",\\n \\\"DEEPINFRA_API_KEY\\\": \\\"your-deepinfra-key\\\",\\n \\\"SAMBANOVA_API_KEY\\\": \\\"your-sambanova-key\\\"\\n }\\n }\\n }\\n}\\n```\\n\\nNote: The environment variables are optional. Only include the API keys for the providers you want to use.\\n\\n## Quick Start\\n\\nOnce installed and configured, you can use the tools in your MCP client:\\n\\n1. **Generate text**: Use the `outsource_text` tool with provider \\\"openai\\\", model \\\"gpt-4o-mini\\\", and prompt \\\"Write a haiku about coding\\\"\\n2. **Generate images**: Use the `outsource_image` tool with provider \\\"openai\\\", model \\\"dall-e-3\\\", and prompt \\\"A futuristic city skyline at sunset\\\"\\n\\n## Tools\\n\\n### outsource_text\\nCreates an Agno agent with a specified provider and model to generate text responses. \\n\\n**Arguments:**\\n- `provider`: The provider name (e.g., \\\"openai\\\", \\\"anthropic\\\", \\\"google\\\", \\\"groq\\\", etc.)\\n- `model`: The model name (e.g., \\\"gpt-4o\\\", \\\"claude-3-5-sonnet-20241022\\\", \\\"gemini-2.0-flash-exp\\\")\\n- `prompt`: The text prompt to send to the model\\n\\n### outsource_image\\nGenerates images using AI models.\\n\\n**Arguments:**\\n- `provider`: The provider name (currently only \\\"openai\\\" is supported)\\n- `model`: The model name (\\\"dall-e-3\\\" or \\\"dall-e-2\\\")\\n- `prompt`: The image generation prompt\\n\\nReturns the URL of the generated image.\\n\\n> **Note**: Image generation is currently only supported by OpenAI models (DALL-E 2 and DALL-E 3). Other providers only support text generation.\\n\\n## Supported Providers\\n\\nThe following providers are supported. Use the provider name (in parentheses) as the `provider` argument:\\n\\n### Core Providers\\n- **OpenAI** (`openai`) - GPT-4, GPT-3.5, DALL-E, etc. | [Models](https://platform.openai.com/docs/models)\\n- **Anthropic** (`anthropic`) - Claude 3.5, Claude 3, etc. | [Models](https://docs.anthropic.com/en/docs/about-claude/models/overview)\\n- **Google** (`google`) - Gemini Pro, Gemini Flash, etc. | [Models](https://ai.google.dev/models)\\n- **Groq** (`groq`) - Llama 3, Mixtral, etc. | [Models](https://console.groq.com/docs/models)\\n- **DeepSeek** (`deepseek`) - DeepSeek Chat & Coder | [Models](https://api-docs.deepseek.com/api/list-models)\\n- **xAI** (`xai`) - Grok models | [Models](https://docs.x.ai/docs/models)\\n- **Perplexity** (`perplexity`) - Sonar models | [Models](https://docs.perplexity.ai/guides/model-cards)\\n\\n### Additional Providers\\n- **Cohere** (`cohere`) - Command models | [Models](https://docs.cohere.com/v2/docs/models)\\n- **Mistral AI** (`mistral`) - Mistral Large, Medium, Small | [Models](https://docs.mistral.ai/getting-started/models/models_overview/)\\n- **NVIDIA** (`nvidia`) - Various models | [Models](https://build.nvidia.com/models)\\n- **HuggingFace** (`huggingface`) - Open source models | [Models](https://huggingface.co/models)\\n- **Ollama** (`ollama`) - Local models | [Models](https://ollama.com/library)\\n- **Fireworks AI** (`fireworks`) - Fast inference | [Models](https://fireworks.ai/models?view=list)\\n- **OpenRouter** (`openrouter`) - Multi-provider access | [Models](https://openrouter.ai/docs/overview/models)\\n- **Together AI** (`together`) - Open source models | [Models](https://docs.together.ai/docs/serverless-models)\\n- **Cerebras** (`cerebras`) - Fast inference | [Models](https://cerebras.ai/models)\\n- **DeepInfra** (`deepinfra`) - Optimized models | [Models](https://deepinfra.com/docs/models)\\n- **SambaNova** (`sambanova`) - Enterprise models | [Models](https://docs.sambanova.ai/cloud/docs/get-started/supported-models)\\n\\n### Enterprise Providers\\n- **AWS Bedrock** (`aws` or `bedrock`) - AWS-hosted models | [Models](https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html)\\n- **Azure AI** (`azure`) - Azure-hosted models | [Models](https://learn.microsoft.com/en-us/azure/ai-foundry/concepts/foundry-models-overview)\\n- **IBM WatsonX** (`ibm` or `watsonx`) - IBM models | [Models](https://www.ibm.com/docs/en/software-hub/5.1.x?topic=install-foundation-models)\\n- **LiteLLM** (`litellm`) - Universal interface | [Models](https://docs.litellm.ai/docs/providers)\\n- **Vercel v0** (`vercel` or `v0`) - Vercel AI | [Models](https://sdk.vercel.ai/docs/introduction)\\n- **Meta Llama** (`meta`) - Direct Meta access | [Models](https://www.llama.com/get-started/)\\n\\n### Environment Variables\\n\\nEach provider requires its corresponding API key:\\n\\n| Provider | Environment Variable | Example |\\n|----------|---------------------|---------|\\n| OpenAI | `OPENAI_API_KEY` | sk-... |\\n| Anthropic | `ANTHROPIC_API_KEY` | sk-ant-... |\\n| Google | `GOOGLE_API_KEY` | AIza... |\\n| Groq | `GROQ_API_KEY` | gsk_... |\\n| DeepSeek | `DEEPSEEK_API_KEY` | sk-... |\\n| xAI | `XAI_API_KEY` | xai-... |\\n| Perplexity | `PERPLEXITY_API_KEY` | pplx-... |\\n| Cohere | `COHERE_API_KEY` | ... |\\n| Fireworks | `FIREWORKS_API_KEY` | ... |\\n| HuggingFace | `HUGGINGFACE_API_KEY` | hf_... |\\n| Mistral | `MISTRAL_API_KEY` | ... |\\n| NVIDIA | `NVIDIA_API_KEY` | nvapi-... |\\n| Ollama | `OLLAMA_HOST` | http://localhost:11434 |\\n| OpenRouter | `OPENROUTER_API_KEY` | ... |\\n| Together | `TOGETHER_API_KEY` | ... |\\n| Cerebras | `CEREBRAS_API_KEY` | ... |\\n| DeepInfra | `DEEPINFRA_API_KEY` | ... |\\n| SambaNova | `SAMBANOVA_API_KEY` | ... |\\n| AWS Bedrock | AWS credentials | Via AWS CLI/SDK |\\n| Azure AI | Azure credentials | Via Azure CLI/SDK |\\n| IBM WatsonX | `IBM_WATSONX_API_KEY` | ... |\\n| Meta Llama | `LLAMA_API_KEY` | ... |\\n\\n**Note**: Only configure the API keys for providers you plan to use.\\n\\n## Examples\\n\\n### Text Generation\\n```\\n# Using OpenAI\\nprovider: openai\\nmodel: gpt-4o-mini\\nprompt: Write a haiku about coding\\n\\n# Using Anthropic\\nprovider: anthropic\\nmodel: claude-3-5-sonnet-20241022\\nprompt: Explain quantum computing in simple terms\\n\\n# Using Google\\nprovider: google\\nmodel: gemini-2.0-flash-exp\\nprompt: Create a recipe for chocolate chip cookies\\n```\\n\\n### Image Generation\\n```\\n# Using DALL-E 3\\nprovider: openai\\nmodel: dall-e-3\\nprompt: A serene Japanese garden with cherry blossoms\\n\\n# Using DALL-E 2\\nprovider: openai\\nmodel: dall-e-2\\nprompt: A futuristic cityscape at sunset\\n```\\n\\n## Development\\n\\n### Prerequisites\\n\\n- Python 3.11 or higher\\n- [uv](https://github.com/astral-sh/uv) package manager\\n\\n### Setup\\n\\n```bash\\ngit clone https://github.com/gwbischof/outsource-mcp.git\\ncd outsource-mcp\\nuv sync\\n```\\n\\n### Testing with MCP Inspector\\n\\nThe MCP Inspector allows you to test the server interactively:\\n\\n```bash\\nmcp dev server.py\\n```\\n\\n### Running Tests\\n\\nThe test suite includes integration tests that verify both text and image generation:\\n\\n```bash\\n# Run all tests\\nuv run pytest\\n```\\n\\n**Note:** Integration tests require API keys to be set in your environment.\\n\\n## Troubleshooting\\n\\n### Common Issues\\n\\n1. **\\\"Error: Unknown provider\\\"**\\n - Check that you're using a supported provider name from the list above\\n - Provider names are case-insensitive\\n\\n2. **\\\"Error: OpenAI API error\\\"** \\n - Verify your API key is correctly set in the environment variables\\n - Check that your API key has access to the requested model\\n - Ensure you have sufficient credits/quota\\n\\n3. **\\\"Error: No image was generated\\\"**\\n - This can happen if the image generation request fails\\n - Try a simpler prompt or different model (dall-e-2 vs dall-e-3)\\n\\n4. **Environment variables not working**\\n - Make sure to restart your MCP client after updating the configuration\\n - Verify the configuration file location for your specific MCP client\\n - Check that the environment variables are properly formatted in the configuration\\n\\n## Contributing\\n\\nContributions are welcome! Please feel free to submit a Pull Request.\\n\",\n \"category\": \"AI Tools\",\n \"archestra_config\": {\n \"client_config_permutations\": {\n \"outsource-mcp\": {\n \"command\": \"uvx\",\n \"args\": [\"--from\", \"git+https://github.com/gwbischof/outsource-mcp.git\", \"outsource-mcp\"],\n \"env\": {\n \"OPENAI_API_KEY\": \"your-openai-key\",\n \"ANTHROPIC_API_KEY\": \"your-anthropic-key\",\n \"GOOGLE_API_KEY\": \"your-google-key\",\n \"GROQ_API_KEY\": \"your-groq-key\",\n \"DEEPSEEK_API_KEY\": \"your-deepseek-key\",\n \"XAI_API_KEY\": \"your-xai-key\",\n \"PERPLEXITY_API_KEY\": \"your-perplexity-key\",\n \"COHERE_API_KEY\": \"your-cohere-key\",\n \"FIREWORKS_API_KEY\": \"your-fireworks-key\",\n \"HUGGINGFACE_API_KEY\": \"your-huggingface-key\",\n \"MISTRAL_API_KEY\": \"your-mistral-key\",\n \"NVIDIA_API_KEY\": \"your-nvidia-key\",\n \"OLLAMA_HOST\": \"http://localhost:11434\",\n \"OPENROUTER_API_KEY\": \"your-openrouter-key\",\n \"TOGETHER_API_KEY\": \"your-together-key\",\n \"CEREBRAS_API_KEY\": \"your-cerebras-key\",\n \"DEEPINFRA_API_KEY\": \"your-deepinfra-key\",\n \"SAMBANOVA_API_KEY\": \"your-sambanova-key\"\n }\n }\n },\n \"oauth\": {\n \"provider\": null,\n \"required\": false\n },\n \"works_in_archestra\": false\n },\n \"github_info\": {\n \"owner\": \"gwbischof\",\n \"repo\": \"outsource-mcp\",\n \"url\": \"https://github.com/gwbischof/outsource-mcp\",\n \"name\": \"gwbischof__outsource-mcp\",\n \"path\": null,\n \"stars\": 22,\n \"contributors\": 1,\n \"issues\": 0,\n \"releases\": false,\n \"ci_cd\": false,\n \"latest_commit_hash\": \"70e7b6b4031d1819fad18ee313fb6ec16e126b92\"\n },\n \"programming_language\": \"Python\"\n}\n"}}, "mcp-catalog/data/mcp-evaluations/othmaneblial__term_mcp_deepseek.json": {"285a975cae91486d95d223be9507021e7d70ac46": {"t": 1785745300.3981304, "n": 5298, "h": "bb04f9f56e5c09b6", "c": "{\n \"name\": \"othmaneblial__term_mcp_deepseek\",\n \"display_name\": \"term_mcp_deepseek\",\n \"description\": \"A MCP‑like server using the DeepSeek API for Terminal\",\n \"author\": {\n \"name\": \"OthmaneBlial\"\n },\n \"server\": {\n \"command\": \"python\",\n \"args\": [\"${__dirname}/server.py\"],\n \"env\": {\n \"DEEPSEEK_API_KEY\": \"${user_config.deepseek_api_key}\"\n },\n \"type\": \"local\"\n },\n \"tools\": [],\n \"prompts\": [],\n \"keywords\": [],\n \"user_config\": {\n \"deepseek_api_key\": {\n \"type\": \"string\",\n \"title\": \"DeepSeek API Key\",\n \"description\": \"Your DeepSeek API key for authentication.\",\n \"sensitive\": true,\n \"required\": true\n }\n },\n \"readme\": \"# DeepSeek MCP-like Server for Terminal\\n\\n[](https://archestra.ai/mcp-catalog/othmaneblial__term_mcp_deepseek)\\nThis project is a prototype implementation of an MCP‑like server using the DeepSeek API. It aims to demonstrate the core concepts behind the Model Context Protocol (MCP) by exposing endpoints that allow AI assistants to:\\n \\n- List available tools.\\n- Invoke commands on an active shell session.\\n- Integrate with an AI chat (DeepSeek) that can include special instructions (e.g., `CMD:` lines) to trigger command execution.\\n\\n> **Note:** While this implementation captures many of the MCP ideas, it is not yet a fully compliant MCP server as defined by Anthropic. It is designed as a proof-of-concept, and further enhancements (e.g., JSON‑RPC protocol support, real‑time updates via SSE, session management, and improved security) would be needed for production use.\\n\\n## Features\\n\\n- **Chat Interface:** \\n A simple web-based chat client (using Flask and Tailwind CSS) where users can interact with the server.\\n\\n- **AI Integration:** \\n Uses the DeepSeek API to generate responses. The AI can instruct the server to execute terminal commands by including lines beginning with `CMD:`.\\n\\n- **Terminal Command Execution:** \\n Executes shell commands via a persistent Bash session using the `pexpect` library and returns output to the client.\\n\\n- **MCP Endpoints:** \\n Provides `/mcp/list_tools` and `/mcp/call_tool` endpoints that mimic MCP tool discovery and invocation.\\n\\n## Getting Started\\n\\n### Prerequisites\\n\\n- Python 3.8+\\n- [pip](https://pip.pypa.io/)\\n- A valid DeepSeek API key\\n\\n### Installation\\n\\n1. **Clone the repository:**\\n\\n ```bash\\n git clone https://github.com/OthmaneBlial/term_mcp_deepseek.git\\n cd term_mcp_deepseek\\n ```\\n\\n2. **Create and activate a virtual environment:**\\n\\n ```bash\\n python3 -m venv venv\\n source venv/bin/activate # On Windows, use `venv\\\\Scripts\\\\activate`\\n ```\\n\\n3. **Install the required dependencies:**\\n\\n ```bash\\n pip install -r requirements.txt\\n ```\\n\\n4. **Configure your API key:**\\n\\n Update the `DEEPSEEK_API_KEY` in `.env` with your DeepSeek API key.\\n\\n### Running the Server\\n\\nRun the Flask server with:\\n\\n```bash\\npython server.py\\n```\\n\\nVisit [http://127.0.0.1:5000](http://127.0.0.1:5000) to access the chat interface.\\n\\n## Endpoints\\n\\n### Chat Endpoint\\n- **URL:** `/chat`\\n- **Method:** `POST`\\n- **Payload:** `{ \\\"message\\\": \\\"your message here\\\" }`\\n- **Description:** \\n Adds the user message to the conversation, sends it to the DeepSeek API, looks for any command instructions (`CMD:`), executes them, and returns the final response.\\n\\n### MCP Endpoints\\n\\n#### List Tools\\n- **URL:** `/mcp/list_tools`\\n- **Method:** `POST`\\n- **Response:** \\n JSON listing available tools (e.g., `write_to_terminal`, `read_terminal_output`, `send_control_character`).\\n\\n#### Call Tool\\n- **URL:** `/mcp/call_tool`\\n- **Method:** `POST`\\n- **Payload:** \\n ```json\\n {\\n \\\"name\\\": \\\"tool_name\\\",\\n \\\"arguments\\\": { ... }\\n }\\n ```\\n- **Description:** \\n Directly invoke a tool command on the server.\\n\\n## Future Improvements\\n\\n- **Protocol Standardization:** \\n Implement JSON‑RPC for a more robust and standardized communication protocol.\\n\\n- **Real-time Communication:** \\n Add Server‑Sent Events (SSE) or WebSockets for live command output streaming.\\n\\n- **Session & Security Enhancements:** \\n Introduce per‑user sessions, proper authentication, input sanitization, and comprehensive error handling.\\n\\n- **Modular Code Architecture:** \\n Further separate API logic from business logic for better maintainability and scalability.\\n\\n## License\\n\\nThis project is open-source and available under the [MIT License](LICENSE).\\n\\n\",\n \"category\": \"AI Tools\",\n \"archestra_config\": {\n \"client_config_permutations\": {\n \"term-mcp-deepseek\": {\n \"command\": \"python\",\n \"args\": [\"server.py\"],\n \"env\": {}\n }\n },\n \"oauth\": {\n \"provider\": null,\n \"required\": false\n },\n \"works_in_archestra\": false\n },\n \"github_info\": {\n \"owner\": \"OthmaneBlial\",\n \"repo\": \"term_mcp_deepseek\",\n \"url\": \"https://github.com/OthmaneBlial/term_mcp_deepseek\",\n \"name\": \"othmaneblial__term_mcp_deepseek\",\n \"path\": null,\n \"stars\": 12,\n \"contributors\": 2,\n \"issues\": 0,\n \"releases\": false,\n \"ci_cd\": false,\n \"latest_commit_hash\": \"bc1836705bb5c6deb5bf8cb8e6a9a159895045a3\"\n },\n \"programming_language\": \"Python\"\n}\n"}, "649d7ed1a02af2490f281b74f479cda426d3c62f": {"t": 1785749855.80199, "n": 5298, "h": "bb04f9f56e5c09b6", "c": "{\n \"name\": \"othmaneblial__term_mcp_deepseek\",\n \"display_name\": \"term_mcp_deepseek\",\n \"description\": \"A MCP‑like server using the DeepSeek API for Terminal\",\n \"author\": {\n \"name\": \"OthmaneBlial\"\n },\n \"server\": {\n \"command\": \"python\",\n \"args\": [\"${__dirname}/server.py\"],\n \"env\": {\n \"DEEPSEEK_API_KEY\": \"${user_config.deepseek_api_key}\"\n },\n \"type\": \"local\"\n },\n \"tools\": [],\n \"prompts\": [],\n \"keywords\": [],\n \"user_config\": {\n \"deepseek_api_key\": {\n \"type\": \"string\",\n \"title\": \"DeepSeek API Key\",\n \"description\": \"Your DeepSeek API key for authentication.\",\n \"sensitive\": true,\n \"required\": true\n }\n },\n \"readme\": \"# DeepSeek MCP-like Server for Terminal\\n\\n[](https://archestra.ai/mcp-catalog/othmaneblial__term_mcp_deepseek)\\nThis project is a prototype implementation of an MCP‑like server using the DeepSeek API. It aims to demonstrate the core concepts behind the Model Context Protocol (MCP) by exposing endpoints that allow AI assistants to:\\n \\n- List available tools.\\n- Invoke commands on an active shell session.\\n- Integrate with an AI chat (DeepSeek) that can include special instructions (e.g., `CMD:` lines) to trigger command execution.\\n\\n> **Note:** While this implementation captures many of the MCP ideas, it is not yet a fully compliant MCP server as defined by Anthropic. It is designed as a proof-of-concept, and further enhancements (e.g., JSON‑RPC protocol support, real‑time updates via SSE, session management, and improved security) would be needed for production use.\\n\\n## Features\\n\\n- **Chat Interface:** \\n A simple web-based chat client (using Flask and Tailwind CSS) where users can interact with the server.\\n\\n- **AI Integration:** \\n Uses the DeepSeek API to generate responses. The AI can instruct the server to execute terminal commands by including lines beginning with `CMD:`.\\n\\n- **Terminal Command Execution:** \\n Executes shell commands via a persistent Bash session using the `pexpect` library and returns output to the client.\\n\\n- **MCP Endpoints:** \\n Provides `/mcp/list_tools` and `/mcp/call_tool` endpoints that mimic MCP tool discovery and invocation.\\n\\n## Getting Started\\n\\n### Prerequisites\\n\\n- Python 3.8+\\n- [pip](https://pip.pypa.io/)\\n- A valid DeepSeek API key\\n\\n### Installation\\n\\n1. **Clone the repository:**\\n\\n ```bash\\n git clone https://github.com/OthmaneBlial/term_mcp_deepseek.git\\n cd term_mcp_deepseek\\n ```\\n\\n2. **Create and activate a virtual environment:**\\n\\n ```bash\\n python3 -m venv venv\\n source venv/bin/activate # On Windows, use `venv\\\\Scripts\\\\activate`\\n ```\\n\\n3. **Install the required dependencies:**\\n\\n ```bash\\n pip install -r requirements.txt\\n ```\\n\\n4. **Configure your API key:**\\n\\n Update the `DEEPSEEK_API_KEY` in `.env` with your DeepSeek API key.\\n\\n### Running the Server\\n\\nRun the Flask server with:\\n\\n```bash\\npython server.py\\n```\\n\\nVisit [http://127.0.0.1:5000](http://127.0.0.1:5000) to access the chat interface.\\n\\n## Endpoints\\n\\n### Chat Endpoint\\n- **URL:** `/chat`\\n- **Method:** `POST`\\n- **Payload:** `{ \\\"message\\\": \\\"your message here\\\" }`\\n- **Description:** \\n Adds the user message to the conversation, sends it to the DeepSeek API, looks for any command instructions (`CMD:`), executes them, and returns the final response.\\n\\n### MCP Endpoints\\n\\n#### List Tools\\n- **URL:** `/mcp/list_tools`\\n- **Method:** `POST`\\n- **Response:** \\n JSON listing available tools (e.g., `write_to_terminal`, `read_terminal_output`, `send_control_character`).\\n\\n#### Call Tool\\n- **URL:** `/mcp/call_tool`\\n- **Method:** `POST`\\n- **Payload:** \\n ```json\\n {\\n \\\"name\\\": \\\"tool_name\\\",\\n \\\"arguments\\\": { ... }\\n }\\n ```\\n- **Description:** \\n Directly invoke a tool command on the server.\\n\\n## Future Improvements\\n\\n- **Protocol Standardization:** \\n Implement JSON‑RPC for a more robust and standardized communication protocol.\\n\\n- **Real-time Communication:** \\n Add Server‑Sent Events (SSE) or WebSockets for live command output streaming.\\n\\n- **Session & Security Enhancements:** \\n Introduce per‑user sessions, proper authentication, input sanitization, and comprehensive error handling.\\n\\n- **Modular Code Architecture:** \\n Further separate API logic from business logic for better maintainability and scalability.\\n\\n## License\\n\\nThis project is open-source and available under the [MIT License](LICENSE).\\n\\n\",\n \"category\": \"AI Tools\",\n \"archestra_config\": {\n \"client_config_permutations\": {\n \"term-mcp-deepseek\": {\n \"command\": \"python\",\n \"args\": [\"server.py\"],\n \"env\": {}\n }\n },\n \"oauth\": {\n \"provider\": null,\n \"required\": false\n },\n \"works_in_archestra\": false\n },\n \"github_info\": {\n \"owner\": \"OthmaneBlial\",\n \"repo\": \"term_mcp_deepseek\",\n \"url\": \"https://github.com/OthmaneBlial/term_mcp_deepseek\",\n \"name\": \"othmaneblial__term_mcp_deepseek\",\n \"path\": null,\n \"stars\": 12,\n \"contributors\": 2,\n \"issues\": 0,\n \"releases\": false,\n \"ci_cd\": false,\n \"latest_commit_hash\": \"bc1836705bb5c6deb5bf8cb8e6a9a159895045a3\"\n },\n \"programming_language\": \"Python\"\n}\n"}, "cc332427558a1893247db3d7326d59192f4fd1bb": {"t": 1785749856.984658, "n": 6167, "h": "5b363adead04dea7", "c": "{\n \"name\": \"othmaneblial__term_mcp_deepseek\",\n \"display_name\": \"term_mcp_deepseek\",\n \"description\": \"A MCP‑like server using the DeepSeek API for Terminal\",\n \"author\": {\n \"name\": \"OthmaneBlial\"\n },\n \"server\": {\n \"command\": \"python\",\n \"args\": [\"${__dirname}/server.py\"],\n \"env\": {\n \"DEEPSEEK_API_KEY\": \"${user_config.deepseek_api_key}\"\n },\n \"type\": \"local\"\n },\n \"tools\": [],\n \"prompts\": [],\n \"keywords\": [],\n \"user_config\": {\n \"deepseek_api_key\": {\n \"type\": \"string\",\n \"title\": \"DeepSeek API Key\",\n \"description\": \"Your DeepSeek API key for authentication.\",\n \"sensitive\": true,\n \"required\": true\n }\n },\n \"readme\": \"# DeepSeek MCP-like Server for Terminal\\n\\n[](https://archestra.ai/mcp-catalog/othmaneblial__term_mcp_deepseek)\\nThis project is a prototype implementation of an MCP‑like server using the DeepSeek API. It aims to demonstrate the core concepts behind the Model Context Protocol (MCP) by exposing endpoints that allow AI assistants to:\\n \\n- List available tools.\\n- Invoke commands on an active shell session.\\n- Integrate with an AI chat (DeepSeek) that can include special instructions (e.g., `CMD:` lines) to trigger command execution.\\n\\n> **Note:** While this implementation captures many of the MCP ideas, it is not yet a fully compliant MCP server as defined by Anthropic. It is designed as a proof-of-concept, and further enhancements (e.g., JSON‑RPC protocol support, real‑time updates via SSE, session management, and improved security) would be needed for production use.\\n\\n## Features\\n\\n- **Chat Interface:** \\n A simple web-based chat client (using Flask and Tailwind CSS) where users can interact with the server.\\n\\n- **AI Integration:** \\n Uses the DeepSeek API to generate responses. The AI can instruct the server to execute terminal commands by including lines beginning with `CMD:`.\\n\\n- **Terminal Command Execution:** \\n Executes shell commands via a persistent Bash session using the `pexpect` library and returns output to the client.\\n\\n- **MCP Endpoints:** \\n Provides `/mcp/list_tools` and `/mcp/call_tool` endpoints that mimic MCP tool discovery and invocation.\\n\\n## Getting Started\\n\\n### Prerequisites\\n\\n- Python 3.8+\\n- [pip](https://pip.pypa.io/)\\n- A valid DeepSeek API key\\n\\n### Installation\\n\\n1. **Clone the repository:**\\n\\n ```bash\\n git clone https://github.com/OthmaneBlial/term_mcp_deepseek.git\\n cd term_mcp_deepseek\\n ```\\n\\n2. **Create and activate a virtual environment:**\\n\\n ```bash\\n python3 -m venv venv\\n source venv/bin/activate # On Windows, use `venv\\\\Scripts\\\\activate`\\n ```\\n\\n3. **Install the required dependencies:**\\n\\n ```bash\\n pip install -r requirements.txt\\n ```\\n\\n4. **Configure your API key:**\\n\\n Update the `DEEPSEEK_API_KEY` in `.env` with your DeepSeek API key.\\n\\n### Running the Server\\n\\nRun the Flask server with:\\n\\n```bash\\npython server.py\\n```\\n\\nVisit [http://127.0.0.1:5000](http://127.0.0.1:5000) to access the chat interface.\\n\\n## Endpoints\\n\\n### Chat Endpoint\\n- **URL:** `/chat`\\n- **Method:** `POST`\\n- **Payload:** `{ \\\"message\\\": \\\"your message here\\\" }`\\n- **Description:** \\n Adds the user message to the conversation, sends it to the DeepSeek API, looks for any command instructions (`CMD:`), executes them, and returns the final response.\\n\\n### MCP Endpoints\\n\\n#### List Tools\\n- **URL:** `/mcp/list_tools`\\n- **Method:** `POST`\\n- **Response:** \\n JSON listing available tools (e.g., `write_to_terminal`, `read_terminal_output`, `send_control_character`).\\n\\n#### Call Tool\\n- **URL:** `/mcp/call_tool`\\n- **Method:** `POST`\\n- **Payload:** \\n ```json\\n {\\n \\\"name\\\": \\\"tool_name\\\",\\n \\\"arguments\\\": { ... }\\n }\\n ```\\n- **Description:** \\n Directly invoke a tool command on the server.\\n\\n## Future Improvements\\n\\n- **Protocol Standardization:** \\n Implement JSON‑RPC for a more robust and standardized communication protocol.\\n\\n- **Real-time Communication:** \\n Add Server‑Sent Events (SSE) or WebSockets for live command output streaming.\\n\\n- **Session & Security Enhancements:** \\n Introduce per‑user sessions, proper authentication, input sanitization, and comprehensive error handling.\\n\\n- **Modular Code Architecture:** \\n Further separate API logic from business logic for better maintainability and scalability.\\n\\n## License\\n\\nThis project is open-source and available under the [MIT License](LICENSE).\\n\\n\",\n \"category\": \"AI Tools\",\n \"quality_score\": 43,\n \"archestra_config\": {\n \"client_config_permutations\": {\n \"term-mcp-deepseek\": {\n \"command\": \"python\",\n \"args\": [\"server.py\"],\n \"env\": {}\n }\n },\n \"oauth\": {\n \"provider\": null,\n \"required\": false\n },\n \"works_in_archestra\": false\n },\n \"github_info\": {\n \"owner\": \"OthmaneBlial\",\n \"repo\": \"term_mcp_deepseek\",\n \"url\": \"https://github.com/OthmaneBlial/term_mcp_deepseek\",\n \"name\": \"othmaneblial__term_mcp_deepseek\",\n \"path\": null,\n \"stars\": 12,\n \"contributors\": 2,\n \"issues\": 0,\n \"releases\": false,\n \"ci_cd\": false,\n \"latest_commit_hash\": \"bc1836705bb5c6deb5bf8cb8e6a9a159895045a3\"\n },\n \"programming_language\": \"Python\",\n \"framework\": null,\n \"last_scraped_at\": \"2025-09-09T13:05:22.000Z\",\n \"evaluation_model\": \"gemini-2.5-flash\",\n \"protocol_features\": {\n \"implementing_tools\": true,\n \"implementing_prompts\": false,\n \"implementing_resources\": false,\n \"implementing_sampling\": true,\n \"implementing_roots\": false,\n \"implementing_logging\": false,\n \"implementing_stdio\": false,\n \"implementing_streamable_http\": false,\n \"implementing_oauth2\": false\n },\n \"dependencies\": [\n {\n \"name\": \"Flask\",\n \"importance\": 10\n },\n {\n \"name\": \"pexpect\",\n \"importance\": 9\n },\n {\n \"name\": \"psutil\",\n \"importance\": 6\n },\n {\n \"name\": \"python-dotenv\",\n \"importance\": 4\n }\n ],\n \"raw_dependencies\": \"=== requirements.txt ===\\nFlask==2.3.3\\npexpect==4.8.0\\npsutil==5.9.5\\npython-dotenv==1.0.0 \\n\"\n}\n"}, "94ef5b65a4a0dffaf09fbeb306798a5f24dd4254": {"t": 1786067344.74384, "n": 5298, "h": "bb04f9f56e5c09b6", "c": "{\n \"name\": \"othmaneblial__term_mcp_deepseek\",\n \"display_name\": \"term_mcp_deepseek\",\n \"description\": \"A MCP‑like server using the DeepSeek API for Terminal\",\n \"author\": {\n \"name\": \"OthmaneBlial\"\n },\n \"server\": {\n \"command\": \"python\",\n \"args\": [\"${__dirname}/server.py\"],\n \"env\": {\n \"DEEPSEEK_API_KEY\": \"${user_config.deepseek_api_key}\"\n },\n \"type\": \"local\"\n },\n \"tools\": [],\n \"prompts\": [],\n \"keywords\": [],\n \"user_config\": {\n \"deepseek_api_key\": {\n \"type\": \"string\",\n \"title\": \"DeepSeek API Key\",\n \"description\": \"Your DeepSeek API key for authentication.\",\n \"sensitive\": true,\n \"required\": true\n }\n },\n \"readme\": \"# DeepSeek MCP-like Server for Terminal\\n\\n[](https://archestra.ai/mcp-catalog/othmaneblial__term_mcp_deepseek)\\nThis project is a prototype implementation of an MCP‑like server using the DeepSeek API. It aims to demonstrate the core concepts behind the Model Context Protocol (MCP) by exposing endpoints that allow AI assistants to:\\n \\n- List available tools.\\n- Invoke commands on an active shell session.\\n- Integrate with an AI chat (DeepSeek) that can include special instructions (e.g., `CMD:` lines) to trigger command execution.\\n\\n> **Note:** While this implementation captures many of the MCP ideas, it is not yet a fully compliant MCP server as defined by Anthropic. It is designed as a proof-of-concept, and further enhancements (e.g., JSON‑RPC protocol support, real‑time updates via SSE, session management, and improved security) would be needed for production use.\\n\\n## Features\\n\\n- **Chat Interface:** \\n A simple web-based chat client (using Flask and Tailwind CSS) where users can interact with the server.\\n\\n- **AI Integration:** \\n Uses the DeepSeek API to generate responses. The AI can instruct the server to execute terminal commands by including lines beginning with `CMD:`.\\n\\n- **Terminal Command Execution:** \\n Executes shell commands via a persistent Bash session using the `pexpect` library and returns output to the client.\\n\\n- **MCP Endpoints:** \\n Provides `/mcp/list_tools` and `/mcp/call_tool` endpoints that mimic MCP tool discovery and invocation.\\n\\n## Getting Started\\n\\n### Prerequisites\\n\\n- Python 3.8+\\n- [pip](https://pip.pypa.io/)\\n- A valid DeepSeek API key\\n\\n### Installation\\n\\n1. **Clone the repository:**\\n\\n ```bash\\n git clone https://github.com/OthmaneBlial/term_mcp_deepseek.git\\n cd term_mcp_deepseek\\n ```\\n\\n2. **Create and activate a virtual environment:**\\n\\n ```bash\\n python3 -m venv venv\\n source venv/bin/activate # On Windows, use `venv\\\\Scripts\\\\activate`\\n ```\\n\\n3. **Install the required dependencies:**\\n\\n ```bash\\n pip install -r requirements.txt\\n ```\\n\\n4. **Configure your API key:**\\n\\n Update the `DEEPSEEK_API_KEY` in `.env` with your DeepSeek API key.\\n\\n### Running the Server\\n\\nRun the Flask server with:\\n\\n```bash\\npython server.py\\n```\\n\\nVisit [http://127.0.0.1:5000](http://127.0.0.1:5000) to access the chat interface.\\n\\n## Endpoints\\n\\n### Chat Endpoint\\n- **URL:** `/chat`\\n- **Method:** `POST`\\n- **Payload:** `{ \\\"message\\\": \\\"your message here\\\" }`\\n- **Description:** \\n Adds the user message to the conversation, sends it to the DeepSeek API, looks for any command instructions (`CMD:`), executes them, and returns the final response.\\n\\n### MCP Endpoints\\n\\n#### List Tools\\n- **URL:** `/mcp/list_tools`\\n- **Method:** `POST`\\n- **Response:** \\n JSON listing available tools (e.g., `write_to_terminal`, `read_terminal_output`, `send_control_character`).\\n\\n#### Call Tool\\n- **URL:** `/mcp/call_tool`\\n- **Method:** `POST`\\n- **Payload:** \\n ```json\\n {\\n \\\"name\\\": \\\"tool_name\\\",\\n \\\"arguments\\\": { ... }\\n }\\n ```\\n- **Description:** \\n Directly invoke a tool command on the server.\\n\\n## Future Improvements\\n\\n- **Protocol Standardization:** \\n Implement JSON‑RPC for a more robust and standardized communication protocol.\\n\\n- **Real-time Communication:** \\n Add Server‑Sent Events (SSE) or WebSockets for live command output streaming.\\n\\n- **Session & Security Enhancements:** \\n Introduce per‑user sessions, proper authentication, input sanitization, and comprehensive error handling.\\n\\n- **Modular Code Architecture:** \\n Further separate API logic from business logic for better maintainability and scalability.\\n\\n## License\\n\\nThis project is open-source and available under the [MIT License](LICENSE).\\n\\n\",\n \"category\": \"AI Tools\",\n \"archestra_config\": {\n \"client_config_permutations\": {\n \"term-mcp-deepseek\": {\n \"command\": \"python\",\n \"args\": [\"server.py\"],\n \"env\": {}\n }\n },\n \"oauth\": {\n \"provider\": null,\n \"required\": false\n },\n \"works_in_archestra\": false\n },\n \"github_info\": {\n \"owner\": \"OthmaneBlial\",\n \"repo\": \"term_mcp_deepseek\",\n \"url\": \"https://github.com/OthmaneBlial/term_mcp_deepseek\",\n \"name\": \"othmaneblial__term_mcp_deepseek\",\n \"path\": null,\n \"stars\": 12,\n \"contributors\": 2,\n \"issues\": 0,\n \"releases\": false,\n \"ci_cd\": false,\n \"latest_commit_hash\": \"bc1836705bb5c6deb5bf8cb8e6a9a159895045a3\"\n },\n \"programming_language\": \"Python\"\n}\n"}, "596a18aa85019433ade3bef2b3745e75ccea5ddc": {"t": 1786068230.2026782, "n": 5298, "h": "bb04f9f56e5c09b6", "c": "{\n \"name\": \"othmaneblial__term_mcp_deepseek\",\n \"display_name\": \"term_mcp_deepseek\",\n \"description\": \"A MCP‑like server using the DeepSeek API for Terminal\",\n \"author\": {\n \"name\": \"OthmaneBlial\"\n },\n \"server\": {\n \"command\": \"python\",\n \"args\": [\"${__dirname}/server.py\"],\n \"env\": {\n \"DEEPSEEK_API_KEY\": \"${user_config.deepseek_api_key}\"\n },\n \"type\": \"local\"\n },\n \"tools\": [],\n \"prompts\": [],\n \"keywords\": [],\n \"user_config\": {\n \"deepseek_api_key\": {\n \"type\": \"string\",\n \"title\": \"DeepSeek API Key\",\n \"description\": \"Your DeepSeek API key for authentication.\",\n \"sensitive\": true,\n \"required\": true\n }\n },\n \"readme\": \"# DeepSeek MCP-like Server for Terminal\\n\\n[](https://archestra.ai/mcp-catalog/othmaneblial__term_mcp_deepseek)\\nThis project is a prototype implementation of an MCP‑like server using the DeepSeek API. It aims to demonstrate the core concepts behind the Model Context Protocol (MCP) by exposing endpoints that allow AI assistants to:\\n \\n- List available tools.\\n- Invoke commands on an active shell session.\\n- Integrate with an AI chat (DeepSeek) that can include special instructions (e.g., `CMD:` lines) to trigger command execution.\\n\\n> **Note:** While this implementation captures many of the MCP ideas, it is not yet a fully compliant MCP server as defined by Anthropic. It is designed as a proof-of-concept, and further enhancements (e.g., JSON‑RPC protocol support, real‑time updates via SSE, session management, and improved security) would be needed for production use.\\n\\n## Features\\n\\n- **Chat Interface:** \\n A simple web-based chat client (using Flask and Tailwind CSS) where users can interact with the server.\\n\\n- **AI Integration:** \\n Uses the DeepSeek API to generate responses. The AI can instruct the server to execute terminal commands by including lines beginning with `CMD:`.\\n\\n- **Terminal Command Execution:** \\n Executes shell commands via a persistent Bash session using the `pexpect` library and returns output to the client.\\n\\n- **MCP Endpoints:** \\n Provides `/mcp/list_tools` and `/mcp/call_tool` endpoints that mimic MCP tool discovery and invocation.\\n\\n## Getting Started\\n\\n### Prerequisites\\n\\n- Python 3.8+\\n- [pip](https://pip.pypa.io/)\\n- A valid DeepSeek API key\\n\\n### Installation\\n\\n1. **Clone the repository:**\\n\\n ```bash\\n git clone https://github.com/OthmaneBlial/term_mcp_deepseek.git\\n cd term_mcp_deepseek\\n ```\\n\\n2. **Create and activate a virtual environment:**\\n\\n ```bash\\n python3 -m venv venv\\n source venv/bin/activate # On Windows, use `venv\\\\Scripts\\\\activate`\\n ```\\n\\n3. **Install the required dependencies:**\\n\\n ```bash\\n pip install -r requirements.txt\\n ```\\n\\n4. **Configure your API key:**\\n\\n Update the `DEEPSEEK_API_KEY` in `.env` with your DeepSeek API key.\\n\\n### Running the Server\\n\\nRun the Flask server with:\\n\\n```bash\\npython server.py\\n```\\n\\nVisit [http://127.0.0.1:5000](http://127.0.0.1:5000) to access the chat interface.\\n\\n## Endpoints\\n\\n### Chat Endpoint\\n- **URL:** `/chat`\\n- **Method:** `POST`\\n- **Payload:** `{ \\\"message\\\": \\\"your message here\\\" }`\\n- **Description:** \\n Adds the user message to the conversation, sends it to the DeepSeek API, looks for any command instructions (`CMD:`), executes them, and returns the final response.\\n\\n### MCP Endpoints\\n\\n#### List Tools\\n- **URL:** `/mcp/list_tools`\\n- **Method:** `POST`\\n- **Response:** \\n JSON listing available tools (e.g., `write_to_terminal`, `read_terminal_output`, `send_control_character`).\\n\\n#### Call Tool\\n- **URL:** `/mcp/call_tool`\\n- **Method:** `POST`\\n- **Payload:** \\n ```json\\n {\\n \\\"name\\\": \\\"tool_name\\\",\\n \\\"arguments\\\": { ... }\\n }\\n ```\\n- **Description:** \\n Directly invoke a tool command on the server.\\n\\n## Future Improvements\\n\\n- **Protocol Standardization:** \\n Implement JSON‑RPC for a more robust and standardized communication protocol.\\n\\n- **Real-time Communication:** \\n Add Server‑Sent Events (SSE) or WebSockets for live command output streaming.\\n\\n- **Session & Security Enhancements:** \\n Introduce per‑user sessions, proper authentication, input sanitization, and comprehensive error handling.\\n\\n- **Modular Code Architecture:** \\n Further separate API logic from business logic for better maintainability and scalability.\\n\\n## License\\n\\nThis project is open-source and available under the [MIT License](LICENSE).\\n\\n\",\n \"category\": \"AI Tools\",\n \"archestra_config\": {\n \"client_config_permutations\": {\n \"term-mcp-deepseek\": {\n \"command\": \"python\",\n \"args\": [\"server.py\"],\n \"env\": {}\n }\n },\n \"oauth\": {\n \"provider\": null,\n \"required\": false\n },\n \"works_in_archestra\": false\n },\n \"github_info\": {\n \"owner\": \"OthmaneBlial\",\n \"repo\": \"term_mcp_deepseek\",\n \"url\": \"https://github.com/OthmaneBlial/term_mcp_deepseek\",\n \"name\": \"othmaneblial__term_mcp_deepseek\",\n \"path\": null,\n \"stars\": 12,\n \"contributors\": 2,\n \"issues\": 0,\n \"releases\": false,\n \"ci_cd\": false,\n \"latest_commit_hash\": \"bc1836705bb5c6deb5bf8cb8e6a9a159895045a3\"\n },\n \"programming_language\": \"Python\"\n}\n"}}, "platform/backend/src/clients/llm-client.test.ts": {"596a18aa85019433ade3bef2b3745e75ccea5ddc": {"t": 1786068456.2220025, "n": 46794, "h": "cdaa4cac9ba12252", "c": "import {\n CHAT_API_KEY_ID_HEADER,\n CHATGPT_SUBSCRIPTION_LABEL,\n EXTERNAL_AGENT_ID_HEADER,\n PROVIDER_BASE_URL_HEADER,\n SESSION_ID_HEADER,\n SOURCE_HEADER,\n UNTRUSTED_CONTEXT_HEADER,\n USER_ID_HEADER,\n} from \"@archestra/shared\";\nimport { generateObject, generateText, streamText } from \"ai\";\nimport { vi } from \"vitest\";\nimport { z } from \"zod\";\nimport { ConversationModel, LlmProviderApiKeyModel } from \"@/models\";\nimport { encodeOpenAiCodexCredential } from \"@/services/openai-codex-credentials\";\nimport { describe, expect, it, test } from \"@/test\";\n\n// Mock the gemini-client module before importing llm-client\nconst mockIsVertexAiEnabled = vi.hoisted(() => vi.fn(() => false));\nconst mockIsAzureOpenAiEntraIdEnabled = vi.hoisted(() => vi.fn(() => false));\n// Capture the fetch option passed to createAnthropic, so the thinking-display\n// injection can be asserted against the body that actually goes out.\nconst capturedCreateAnthropicOptions = vi.hoisted(() => ({\n fetch: undefined as typeof globalThis.fetch | undefined,\n}));\nconst mockCreateAnthropic = vi.hoisted(() =>\n vi.fn(\n ({\n headers,\n fetch,\n }: {\n headers?: Record<string, string>;\n fetch?: typeof globalThis.fetch;\n }) => {\n capturedCreateAnthropicOptions.fetch = fetch;\n return vi.fn((modelName: string) => ({\n provider: \"anthropic\",\n modelName,\n headers,\n }));\n },\n ),\n);\nvi.mock(\"@/clients/gemini-client\", () => ({\n isVertexAiEnabled: mockIsVertexAiEnabled,\n}));\nvi.mock(\"@/clients/azure-openai-credentials\", async (importOriginal) => {\n const actual =\n await importOriginal<typeof import(\"@/clients/azure-openai-credentials\")>();\n return {\n ...actual,\n isAzureOpenAiEntraIdEnabled: mockIsAzureOpenAiEntraIdEnabled,\n };\n});\nvi.mock(\"@ai-sdk/anthropic\", () => ({\n createAnthropic: mockCreateAnthropic,\n}));\n\n// Capture the fetch option passed to createOpenAI for azure fetchWithVersion tests\nconst capturedCreateOpenAIOptions = vi.hoisted(() => ({\n fetch: undefined as typeof globalThis.fetch | undefined,\n headers: undefined as Record<string, string> | undefined,\n apiKey: undefined as string | undefined,\n}));\nvi.mock(\"@ai-sdk/openai\", async (importOriginal) => {\n const actual = await importOriginal<typeof import(\"@ai-sdk/openai\")>();\n return {\n ...actual,\n createOpenAI: (options: Parameters<typeof actual.createOpenAI>[0]) => {\n capturedCreateOpenAIOptions.fetch = (\n options as { fetch?: typeof globalThis.fetch }\n ).fetch;\n capturedCreateOpenAIOptions.headers = (\n options as { headers?: Record<string, string> }\n ).headers;\n capturedCreateOpenAIOptions.apiKey = (\n options as { apiKey?: string }\n ).apiKey;\n return actual.createOpenAI(options);\n },\n };\n});\n\n// Capture the options passed to createOpenAICompatible for the azure\n// open-model (reasoning_content) branch tests.\nconst capturedCreateOpenAICompatibleOptions = vi.hoisted(() => ({\n headers: undefined as Record<string, string> | undefined,\n apiKey: undefined as string | undefined,\n includeUsage: undefined as boolean | undefined,\n}));\nvi.mock(\"@ai-sdk/openai-compatible\", async (importOriginal) => {\n const actual =\n await importOriginal<typeof import(\"@ai-sdk/openai-compatible\")>();\n return {\n ...actual,\n createOpenAICompatible: (\n options: Parameters<typeof actual.createOpenAICompatible>[0],\n ) => {\n capturedCreateOpenAICompatibleOptions.headers = (\n options as { headers?: Record<string, string> }\n ).headers;\n capturedCreateOpenAICompatibleOptions.apiKey = (\n options as { apiKey?: string }\n ).apiKey;\n capturedCreateOpenAICompatibleOptions.includeUsage = (\n options as { includeUsage?: boolean }\n ).includeUsage;\n return actual.createOpenAICompatible(options);\n },\n };\n});\n\n// Capture the fetch option passed to createOllama, so the native `think`\n// reconciliation can be asserted against the body that actually goes out.\nconst capturedCreateOllamaOptions = vi.hoisted(() => ({\n fetch: undefined as typeof globalThis.fetch | undefined,\n}));\nvi.mock(\"ollama-ai-provider-v2\", async (importOriginal) => {\n const actual = await importOriginal<typeof import(\"ollama-ai-provider-v2\")>();\n return {\n ...actual,\n createOllama: (options: Parameters<typeof actual.createOllama>[0]) => {\n capturedCreateOllamaOptions.fetch = (\n options as { fetch?: typeof globalThis.fetch }\n ).fetch;\n return actual.createOllama(options);\n },\n };\n});\n\nimport { buildOllamaNativeProviderOptions } from \"@/routes/chat/ollama-native-params\";\nimport type { ConfiguredParameters } from \"@/types/model\";\nimport {\n buildProxyBaseUrl,\n createDirectLLMModel,\n createLLMModel,\n createLLMModelForAgent,\n OLLAMA_THINK_EXPLICIT_HEADER,\n} from \"./llm-client\";\n\ndescribe(\"buildProxyBaseUrl\", () => {\n // The proxy this URL points at runs in this very process, and the API binds\n // IPv4 only (config.api.host is \"0.0.0.0\" outside development). `localhost`\n // resolves to ::1 first under RFC 6724 precedence, so naming it would send\n // every chat turn's first proxy connection to an address with no listener —\n // refused outright without Happy Eyeballs, and a wasted round trip with it.\n // See issues #4917 / #6933 and LOOPBACK_HOST in shared/consts.ts.\n it(\"addresses the loopback interface by IPv4 literal, never by name\", () => {\n const url = new URL(buildProxyBaseUrl(\"anthropic\", \"agent-1\"));\n\n expect(url.hostname).toBe(\"127.0.0.1\");\n expect(url.pathname).toBe(\"/v1/anthropic/agent-1\");\n });\n});\n\ndescribe(\"createDirectLLMModel\", () => {\n it(\"creates a model for anthropic provider\", () => {\n const model = createDirectLLMModel({\n provider: \"anthropic\",\n apiKey: \"test-key\",\n modelName: \"claude-3-5-haiku-20241022\",\n baseUrl: null,\n });\n expect(model).toBeDefined();\n });\n\n it(\"creates a model for openai provider\", () => {\n const model = createDirectLLMModel({\n provider: \"openai\",\n apiKey: \"test-key\",\n modelName: \"gpt-4o-mini\",\n baseUrl: null,\n });\n expect(model).toBeDefined();\n });\n\n it(\"creates a model for gemini provider\", () => {\n const model = createDirectLLMModel({\n provider: \"gemini\",\n apiKey: \"test-key\",\n modelName: \"gemini-1.5-flash\",\n baseUrl: null,\n });\n expect(model).toBeDefined();\n });\n\n it(\"creates a model for cerebras provider\", () => {\n const model = createDirectLLMModel({\n provider: \"cerebras\",\n apiKey: \"test-key\",\n modelName: \"llama-3.3-70b\",\n baseUrl: null,\n });\n expect(model).toBeDefined();\n });\n\n it(\"creates a model for cohere provider\", () => {\n const model = createDirectLLMModel({\n provider: \"cohere\",\n apiKey: \"test-key\",\n modelName: \"command-light\",\n baseUrl: null,\n });\n expect(model).toBeDefined();\n });\n\n it(\"creates keyless vLLM models on the openai-compatible provider\", () => {\n const model = createDirectLLMModel({\n provider: \"vllm\",\n apiKey: undefined,\n modelName: \"deepseek-ai/DeepSeek-R1\",\n baseUrl: \"http://localhost:8000/v1\",\n });\n // vLLM serves DeepSeek-style reasoning models that stream\n // `reasoning_content` and expect it passed back on tool-call turns; the\n // strict openai provider (\"openai.chat\") drops it in both directions,\n // while the openai-compatible provider round-trips it.\n expect((model as { provider: string }).provider).toBe(\"vllm.chat\");\n });\n\n it(\"rejects vllm models without a base URL\", () => {\n // vLLM has no default base URL; without the guard a missing URL would\n // silently route the request to api.openai.com.\n expect(() =>\n createDirectLLMModel({\n provider: \"vllm\",\n apiKey: undefined,\n modelName: \"default\",\n baseUrl: null,\n }),\n ).toThrow(\"vLLM base URL is required.\");\n });\n\n it(\"creates a model for ollama provider without API key\", () => {\n const model = createDirectLLMModel({\n provider: \"ollama\",\n apiKey: undefined,\n modelName: \"llama3.2\",\n baseUrl: null,\n });\n expect(model).toBeDefined();\n });\n\n it(\"creates DeepSeek models on the openai-compatible provider\", () => {\n const model = createDirectLLMModel({\n provider: \"deepseek\",\n apiKey: \"test-key\",\n modelName: \"deepseek-reasoner\",\n baseUrl: null,\n });\n // DeepSeek thinking mode requires `reasoning_content` passed back on\n // tool-call turns; the strict openai provider (\"openai.chat\") drops it in\n // both directions, while the openai-compatible provider round-trips it.\n expect((model as { provider: string }).provider).toBe(\"deepseek.chat\");\n });\n\n it(\"creates Archestra models on the openai-compatible provider\", () => {\n const model = createDirectLLMModel({\n provider: \"archestra\",\n apiKey: \"arch_test-key\",\n modelName: \"deepseek:deepseek-reasoner\",\n baseUrl: \"https://other-archestra.test/v1/model-router/agent-1\",\n });\n // The upstream Archestra relays thinking as DeepSeek-style\n // `reasoning_content`; the strict openai provider (\"openai.chat\") drops\n // that field in both directions, while the openai-compatible provider\n // round-trips it.\n expect((model as { provider: string }).provider).toBe(\"archestra.chat\");\n });\n\n it(\"throws for archestra provider without a base URL\", () => {\n // The upstream endpoint has no default; the old strict client silently\n // fell back to api.openai.com instead.\n expect(() =>\n createDirectLLMModel({\n provider: \"archestra\",\n apiKey: \"arch_test-key\",\n modelName: \"deepseek:deepseek-reasoner\",\n baseUrl: null,\n }),\n ).toThrow(\"Archestra base URL is required.\");\n });\n\n it(\"sends the json_schema response_format for Archestra structured outputs\", async () => {\n let sent: Record<string, unknown> | undefined;\n const mockFetch = vi.fn(\n async (_input: unknown, init: RequestInit | undefined) => {\n sent = JSON.parse(init?.body as string);\n return new Response(\"upstream stub\", { status: 500 });\n },\n );\n // Must be stubbed BEFORE the model is built: createResponseHealingFetch\n // captures `globalThis.fetch` at construction time.\n vi.stubGlobal(\"fetch\", mockFetch);\n\n const model = createDirectLLMModel({\n provider: \"archestra\",\n apiKey: \"arch_test-key\",\n modelName: \"deepseek:deepseek-reasoner\",\n baseUrl: \"https://other-archestra.test/v1/model-router/agent-1\",\n });\n\n // The stubbed upstream fails the call; the request body is captured\n // before that, which is all this assertion needs.\n await generateObject({\n model,\n schema: z.object({ answer: z.number() }),\n prompt: \"2 + 2?\",\n maxRetries: 0,\n }).catch(() => {});\n\n if (!sent) {\n throw new Error(\"Expected a request to reach the fetch stub\");\n }\n // Moving off the strict openai client must not drop the schema:\n // generateObject flows (KB reranker, dual-LLM subagents) rely on the\n // provider enforcing it, and the compatible client otherwise downgrades to\n // a schema-less `json_object`.\n const responseFormat = (\n sent as {\n response_format?: { type?: string; json_schema?: { schema?: unknown } };\n }\n ).response_format;\n expect(responseFormat?.type).toBe(\"json_schema\");\n expect(responseFormat?.json_schema?.schema).toBeDefined();\n });\n\n it(\"creates Kimi models on the openai-compatible provider\", () => {\n const model = createDirectLLMModel({\n provider: \"kimi\",\n apiKey: \"test-key\",\n modelName: \"kimi-k3\",\n baseUrl: null,\n });\n // Kimi thinking models stream reasoning in `reasoning_content` and expect\n // it passed back on tool-call turns; the strict openai provider\n // (\"openai.chat\") drops it in both directions, while the openai-compatible\n // provider round-trips it.\n expect((model as { provider: string }).provider).toBe(\"kimi.chat\");\n });\n\n it(\"creates MiniMax models on the openai-compatible provider\", () => {\n const model = createDirectLLMModel({\n provider: \"minimax\",\n apiKey: \"test-key\",\n modelName: \"MiniMax-M2.5\",\n baseUrl: null,\n });\n // The proxy's minimax adapter surfaces thinking as DeepSeek-style\n // `reasoning_content`; the strict openai provider (\"openai.chat\") drops\n // that field in both directions, while the openai-compatible provider\n // round-trips it.\n expect((model as { provider: string }).provider).toBe(\"minimax.chat\");\n });\n\n it(\"creates OpenRouter models on the openai-compatible provider\", () => {\n const model = createDirectLLMModel({\n provider: \"openrouter\",\n apiKey: \"test-key\",\n modelName: \"deepseek/deepseek-r1\",\n baseUrl: null,\n });\n // OpenRouter streams thinking as a `reasoning` delta field; the strict\n // openai provider (\"openai.chat\") drops it, while the openai-compatible\n // provider surfaces it as reasoning parts.\n expect((model as { provider: string }).provider).toBe(\"openrouter.chat\");\n });\n\n it(\"sends the json_schema response_format for OpenRouter structured outputs\", async () => {\n let sent: Record<string, unknown> | undefined;\n const mockFetch = vi.fn(\n async (_input: unknown, init: RequestInit | undefined) => {\n sent = JSON.parse(init?.body as string);\n return new Response(\"upstream stub\", { status: 500 });\n },\n );\n // Must be stubbed BEFORE the model is built: createResponseHealingFetch\n // captures `globalThis.fetch` at construction time.\n vi.stubGlobal(\"fetch\", mockFetch);\n\n const model = createDirectLLMModel({\n provider: \"openrouter\",\n apiKey: \"test-key\",\n modelName: \"deepseek/deepseek-r1\",\n baseUrl: null,\n });\n\n // The stubbed upstream fails the call; the request body is captured\n // before that, which is all this assertion needs.\n await generateObject({\n model,\n schema: z.object({ answer: z.number() }),\n prompt: \"2 + 2?\",\n maxRetries: 0,\n }).catch(() => {});\n\n if (!sent) {\n throw new Error(\"Expected a request to reach the fetch stub\");\n }\n // generateObject flows (KB reranker, dual-LLM subagents) rely on the\n // provider enforcing the schema: without supportsStructuredOutputs the\n // compatible client downgrades to a schema-less `json_object` response\n // format, and nothing else carries the schema to the model.\n const responseFormat = (\n sent as {\n response_format?: { type?: string; json_schema?: { schema?: unknown } };\n }\n ).response_format;\n expect(responseFormat?.type).toBe(\"json_schema\");\n expect(responseFormat?.json_schema?.schema).toBeDefined();\n });\n\n it(\"sends the json_schema response_format for Azure open-model structured outputs\", async () => {\n let sent: Record<string, unknown> | undefined;\n const mockFetch = vi.fn(\n async (_input: unknown, init: RequestInit | undefined) => {\n sent = JSON.parse(init?.body as string);\n return new Response(\"upstream stub\", { status: 500 });\n },\n );\n vi.stubGlobal(\"fetch\", mockFetch);\n\n const model = createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"test-key\",\n modelName: \"DeepSeek-R1\",\n baseUrl: \"https://my-resource.services.ai.azure.com/openai/v1\",\n });\n\n await generateObject({\n model,\n schema: z.object({ answer: z.number() }),\n prompt: \"2 + 2?\",\n maxRetries: 0,\n }).catch(() => {});\n\n if (!sent) {\n throw new Error(\"Expected a request to reach the fetch stub\");\n }\n // Azure open models moved from the strict openai client, which always sent\n // json_schema, to the compatible one, which downgrades to a schema-less\n // `json_object` unless asked — silently breaking every generateObject flow\n // (KB reranker, dual-LLM subagents) pointed at an Azure deployment.\n const responseFormat = (\n sent as {\n response_format?: { type?: string; json_schema?: { schema?: unknown } };\n }\n ).response_format;\n expect(responseFormat?.type).toBe(\"json_schema\");\n expect(responseFormat?.json_schema?.schema).toBeDefined();\n });\n\n it(\"creates a model for zhipuai provider\", () => {\n const model = createDirectLLMModel({\n provider: \"zhipuai\",\n apiKey: \"test-key\",\n modelName: \"glm-4-flash\",\n baseUrl: null,\n });\n expect(model).toBeDefined();\n });\n\n it(\"throws ApiError for unsupported provider\", () => {\n expect(() =>\n createDirectLLMModel({\n provider: \"unsupported\" as never,\n apiKey: \"test-key\",\n modelName: \"some-model\",\n baseUrl: null,\n }),\n ).toThrow(\"Unsupported provider: unsupported\");\n });\n\n it(\"throws descriptive error for gemini provider without API key and Vertex AI disabled\", () => {\n expect(() =>\n createDirectLLMModel({\n provider: \"gemini\",\n apiKey: undefined,\n modelName: \"gemini-1.5-flash\",\n baseUrl: null,\n }),\n ).toThrow(\n \"Gemini API key is required when Vertex AI is not enabled. Please configure GEMINI_API_KEY or enable Vertex AI.\",\n );\n });\n\n it(\"throws descriptive error for anthropic provider without API key\", () => {\n expect(() =>\n createDirectLLMModel({\n provider: \"anthropic\",\n apiKey: undefined,\n modelName: \"claude-3-5-haiku-20241022\",\n baseUrl: null,\n }),\n ).toThrow(\n \"Anthropic API key is required. Please configure ANTHROPIC_API_KEY.\",\n );\n });\n\n it(\"throws descriptive error for openai provider without API key\", () => {\n expect(() =>\n createDirectLLMModel({\n provider: \"openai\",\n apiKey: undefined,\n modelName: \"gpt-4o-mini\",\n baseUrl: null,\n }),\n ).toThrow(\"OpenAI API key is required. Please configure OPENAI_API_KEY.\");\n });\n\n it(\"creates a model for azure provider\", () => {\n const model = createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"test-key\",\n modelName: \"gpt-4o\",\n baseUrl: \"https://my-resource.openai.azure.com/openai/deployments/gpt-4o\",\n });\n expect(model).toBeDefined();\n expect(capturedCreateOpenAIOptions.headers).toEqual(\n expect.objectContaining({\n \"api-key\": \"test-key\",\n }),\n );\n expect(capturedCreateOpenAIOptions.apiKey).toBe(\"test-key\");\n });\n\n it(\"strips a Bearer prefix before setting the azure api-key header\", () => {\n createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"Bearer test-key\",\n modelName: \"gpt-4o\",\n baseUrl: \"https://my-resource.openai.azure.com/openai/deployments/gpt-4o\",\n });\n\n expect(capturedCreateOpenAIOptions.headers).toEqual(\n expect.objectContaining({\n \"api-key\": \"test-key\",\n }),\n );\n expect(capturedCreateOpenAIOptions.apiKey).toBe(\"test-key\");\n });\n\n it(\"keeps azure OpenAI first-party deployments on the strict openai chat model\", () => {\n const model = createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"test-key\",\n modelName: \"gpt-5.5\",\n baseUrl: \"https://my-resource.services.ai.azure.com/openai/v1\",\n });\n\n expect((model as { provider: string }).provider).toBe(\"openai.chat\");\n });\n\n it(\"uses the openai-compatible chat model for azure open-model deployments so reasoning_content surfaces\", () => {\n const model = createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"Bearer test-key\",\n modelName: \"DeepSeek-R1\",\n baseUrl: \"https://my-resource.services.ai.azure.com/openai/v1\",\n });\n\n expect((model as { provider: string }).provider).toBe(\"azure.chat\");\n expect((model as { modelId: string }).modelId).toBe(\"DeepSeek-R1\");\n // The azure specifics must survive the SDK switch: api-key header\n // (Bearer-stripped) and the usage chunk the strict SDK sends by default.\n expect(capturedCreateOpenAICompatibleOptions.headers).toEqual(\n expect.objectContaining({\n \"api-key\": \"test-key\",\n }),\n );\n expect(capturedCreateOpenAICompatibleOptions.apiKey).toBe(\"test-key\");\n expect(capturedCreateOpenAICompatibleOptions.includeUsage).toBe(true);\n });\n\n describe(\"azure direct base URL handling\", () => {\n // Captures the URL the AI SDK actually requests. The stubbed 500 fails the\n // call; the URL is captured before that, which is all these tests need.\n const requestedUrlFor = async (params: {\n modelName: string;\n baseUrl: string;\n }) => {\n const mockFetch = vi\n .fn()\n .mockResolvedValue(new Response(\"upstream stub\", { status: 500 }));\n vi.stubGlobal(\"fetch\", mockFetch);\n\n const model = createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"test-key\",\n modelName: params.modelName,\n baseUrl: params.baseUrl,\n });\n await generateText({\n model,\n prompt: \"hi\",\n maxRetries: 0,\n }).catch(() => {});\n\n const input = mockFetch.mock.calls[0]?.[0];\n if (!input) {\n throw new Error(\"Expected a request to reach the fetch stub\");\n }\n return new URL(typeof input === \"string\" ? input : input.url);\n };\n\n it(\"does not append api-version to Foundry v1 endpoints\", async () => {\n // The v1 endpoint rejects date-based api-version values with\n // \"API version not supported\", which broke every direct-path call\n // (KB reranker verification, title generation) against v1 base URLs.\n const url = await requestedUrlFor({\n modelName: \"gpt-4.1\",\n baseUrl: \"https://my-resource.cognitiveservices.azure.com/openai/v1\",\n });\n expect(url.pathname).toBe(\"/openai/v1/chat/completions\");\n expect(url.searchParams.has(\"api-version\")).toBe(false);\n });\n\n it(\"does not append api-version for open-model deployments on v1 endpoints\", async () => {\n // Same guard on the openai-compatible branch.\n const url = await requestedUrlFor({\n modelName: \"DeepSeek-R1\",\n baseUrl: \"https://my-resource.cognitiveservices.azure.com/openai/v1\",\n });\n expect(url.pathname).toBe(\"/openai/v1/chat/completions\");\n expect(url.searchParams.has(\"api-version\")).toBe(false);\n });\n\n it(\"routes classic endpoints through the deployment path with api-version\", async () => {\n // Classic resources only serve chat completions under\n // /openai/deployments/<deployment>; posting to <base>/chat/completions\n // 404s (\"Resource not found\").\n const url = await requestedUrlFor({\n modelName: \"gpt-4.1\",\n baseUrl: \"https://my-resource.openai.azure.com/openai\",\n });\n expect(url.pathname).toBe(\"/openai/deployments/gpt-4.1/chat/completions\");\n expect(url.searchParams.has(\"api-version\")).toBe(true);\n });\n\n it(\"keeps deployment-scoped base URLs as-is with api-version\", async () => {\n const url = await requestedUrlFor({\n modelName: \"gpt-4o\",\n baseUrl:\n \"https://my-resource.openai.azure.com/openai/deployments/gpt-4o\",\n });\n expect(url.pathname).toBe(\"/openai/deployments/gpt-4o/chat/completions\");\n expect(url.searchParams.has(\"api-version\")).toBe(true);\n });\n });\n\n it(\"throws when azure has no base URL instead of falling back to api.openai.com\", () => {\n // Covers the strict-SDK branch too: createOpenAI would otherwise default\n // to api.openai.com and send the Azure api-key there.\n expect(() =>\n createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"test-key\",\n modelName: \"gpt-5.5\",\n baseUrl: null,\n }),\n ).toThrow(\"Azure AI Foundry base URL is required.\");\n });\n\n it(\"routes azure gpt-oss deployments through the openai-compatible chat model\", () => {\n const model = createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"test-key\",\n modelName: \"gpt-oss-120b\",\n baseUrl: \"https://my-resource.services.ai.azure.com/openai/v1\",\n });\n\n expect((model as { provider: string }).provider).toBe(\"azure.chat\");\n });\n\n // Boundary test: drive the REAL @ai-sdk/openai-compatible model against a\n // stubbed Azure upstream. Pins the reason for the azure SDK gate: DeepSeek-R1\n // style deployments stream their thinking in `reasoning_content` delta\n // fields, which the strict @ai-sdk/openai chat parser silently drops — the\n // thinking must instead surface as reasoning stream parts.\n it(\"surfaces azure reasoning_content deltas as reasoning stream parts\", async () => {\n const chunk = (payload: object) =>\n `data: ${JSON.stringify({\n id: \"cmpl-1\",\n object: \"chat.completion.chunk\",\n created: 1,\n model: \"DeepSeek-R1\",\n ...payload,\n })}`;\n const sseBody = [\n chunk({\n choices: [\n {\n index: 0,\n delta: { role: \"assistant\", reasoning_content: \"Let me think.\" },\n finish_reason: null,\n },\n ],\n }),\n chunk({\n choices: [{ index: 0, delta: { content: \"42\" }, finish_reason: null }],\n }),\n chunk({ choices: [{ index: 0, delta: {}, finish_reason: \"stop\" }] }),\n chunk({\n choices: [],\n usage: { prompt_tokens: 3, completion_tokens: 5 },\n }),\n \"data: [DONE]\",\n ].join(\"\\n\\n\");\n vi.stubGlobal(\n \"fetch\",\n vi.fn(\n async () =>\n new Response(`${sseBody}\\n\\n`, {\n status: 200,\n headers: { \"content-type\": \"text/event-stream\" },\n }),\n ),\n );\n\n const model = createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"test-key\",\n modelName: \"DeepSeek-R1\",\n baseUrl: \"https://my-resource.services.ai.azure.com/openai/v1\",\n });\n\n const result = streamText({\n model,\n prompt: \"What is 6 * 7?\",\n maxRetries: 0,\n });\n let reasoningText = \"\";\n let answerText = \"\";\n const errors: unknown[] = [];\n for await (const part of result.fullStream) {\n if (part.type === \"reasoning-delta\") reasoningText += part.text;\n if (part.type === \"text-delta\") answerText += part.text;\n if (part.type === \"error\") errors.push(part.error);\n }\n\n expect(errors).toEqual([]);\n expect(reasoningText).toBe(\"Let me think.\");\n expect(answerText).toBe(\"42\");\n });\n\n // createDirectLLMModel doesn't expose a `fetch` parameter — the azure createModel\n // closure always uses `providedFetch ?? globalThis.fetch`. We stub globalThis.fetch\n // to observe the URL that fetchWithVersion passes through.\n // ollama-ai-provider-v2 emits `think: ollamaOptions?.think ?? false`, turning\n // \"the caller said nothing\" into an explicit `think: false` on the wire. That\n // disables thinking, and a qwen3-class model then returns its chain of thought\n // as message `content` closed by a bare `</think>` — which renders as the\n // answer. These pin the reconciliation against the real request body, since\n // asserting on the provider-options bag alone passed while the wire was wrong.\n describe(\"ollama-native think reconciliation\", () => {\n // Boundary test: drive the REAL ollama-ai-provider-v2 and assert on the body\n // it actually builds. The previous version of these tests hand-wrote the\n // request body, so it could not see that the package strips unknown keys\n // from `providerOptions.ollama.options` — the explicit-thinking marker used\n // to ride there, never survived the parse, and `think` was dropped on every\n // setting. The marker is a header now precisely because headers bypass that\n // Zod parse; a hand-written body would once again prove nothing.\n const wireBodyFor = async (\n configured: ConfiguredParameters | null,\n ): Promise<Record<string, unknown>> => {\n let sent: Record<string, unknown> | undefined;\n const mockFetch = vi.fn(\n async (_input: unknown, init: RequestInit | undefined) => {\n sent = JSON.parse(init?.body as string);\n return new Response(\"upstream stub\", { status: 500 });\n },\n );\n // Must be stubbed BEFORE the model is built: createOllamaNativeFetch\n // captures `globalThis.fetch` at construction time.\n vi.stubGlobal(\"fetch\", mockFetch);\n\n const turn = buildOllamaNativeProviderOptions({ configured });\n const model = createDirectLLMModel({\n provider: \"ollama-native\",\n apiKey: undefined,\n modelName: \"qwen3:4b\",\n baseUrl: \"http://localhost:11434\",\n });\n\n // The stubbed upstream fails the call; the request body is captured\n // before that, which is all these assertions need.\n await generateText({\n model,\n prompt: \"hi\",\n maxRetries: 0,\n ...(turn ? { providerOptions: turn.providerOptions } : {}),\n ...(turn?.headers ? { headers: turn.headers } : {}),\n }).catch(() => {});\n\n if (!sent) {\n throw new Error(\"Expected a request to reach the fetch wrapper\");\n }\n return sent;\n };\n\n it(\"omits think entirely when no thinking choice was configured\", async () => {\n // The package defaults `think` to false, which actively DISABLES thinking\n // rather than deferring to the model — a qwen3-class model then returns\n // its chain of thought as message `content` closed by a bare `</think>`.\n const body = await wireBodyFor({ num_ctx: 4096 });\n expect(body).not.toHaveProperty(\"think\");\n });\n\n it(\"sends think:false when an admin explicitly turned thinking off\", async () => {\n const body = await wireBodyFor({ reasoning_effort: \"none\" });\n expect(body.think).toBe(false);\n });\n\n it(\"sends think:true when an admin explicitly turned thinking on\", async () => {\n const body = await wireBodyFor({ reasoning_effort: \"medium\" });\n expect(body.think).toBe(true);\n });\n\n it(\"never leaks the internal marker header upstream\", async () => {\n let sentHeaders: Record<string, string> | undefined;\n const mockFetch = vi.fn(\n async (_input: unknown, init: RequestInit | undefined) => {\n sentHeaders = init?.headers as Record<string, string>;\n return new Response(\"upstream stub\", { status: 500 });\n },\n );\n vi.stubGlobal(\"fetch\", mockFetch);\n\n const turn = buildOllamaNativeProviderOptions({\n configured: { reasoning_effort: \"none\" },\n });\n const model = createDirectLLMModel({\n provider: \"ollama-native\",\n apiKey: undefined,\n modelName: \"qwen3:4b\",\n baseUrl: \"http://localhost:11434\",\n });\n await generateText({\n model,\n prompt: \"hi\",\n maxRetries: 0,\n providerOptions: turn?.providerOptions,\n headers: turn?.headers,\n }).catch(() => {});\n\n const keys = Object.keys(sentHeaders ?? {}).map((k) => k.toLowerCase());\n expect(keys).not.toContain(OLLAMA_THINK_EXPLICIT_HEADER);\n });\n\n it(\"keeps the configured sampling options on the wire\", async () => {\n const body = await wireBodyFor({ num_ctx: 4096, top_k: 40 });\n expect(body.options).toMatchObject({ num_ctx: 4096, top_k: 40 });\n });\n\n it(\"passes a non-JSON body through untouched\", async () => {\n const mockFetch = vi.fn().mockResolvedValue(new Response(\"{}\"));\n vi.stubGlobal(\"fetch\", mockFetch);\n\n createDirectLLMModel({\n provider: \"ollama-native\",\n apiKey: undefined,\n modelName: \"qwen3:4b\",\n baseUrl: \"http://localhost:11434\",\n });\n const wrapped = capturedCreateOllamaOptions.fetch;\n if (!wrapped) {\n throw new Error(\n \"Expected ollama-native fetch wrapper to be configured\",\n );\n }\n\n await wrapped(\"http://localhost:11434/api/chat\", {\n method: \"POST\",\n body: \"not json\",\n });\n\n expect(mockFetch.mock.calls[0][1].body).toBe(\"not json\");\n });\n });\n\n // Sonnet 5 / Fable 5-class models think — and bill those tokens — on every\n // request, but `display` defaults to \"omitted\", so without an injected\n // `thinking` config the response carries only empty signature blocks and\n // the chat UI has nothing to render. The installed @ai-sdk/anthropic has no\n // `display` provider option (its closed Zod schema strips unknown thinking\n // keys), so the injection happens in a fetch wrapper against the raw\n // request body — these tests pin that wire-level rewrite.\n describe(\"anthropic thinking display injection\", () => {\n const sendBody = async (body: string): Promise<RequestInit> => {\n const mockFetch = vi.fn().mockResolvedValue(new Response(\"{}\"));\n // Must be stubbed BEFORE the model is built: the wrapper captures\n // globalThis.fetch at construction time.\n vi.stubGlobal(\"fetch\", mockFetch);\n\n createDirectLLMModel({\n provider: \"anthropic\",\n apiKey: \"test-key\",\n modelName: \"claude-sonnet-5\",\n baseUrl: null,\n });\n const wrapped = capturedCreateAnthropicOptions.fetch;\n if (!wrapped) {\n throw new Error(\"Expected anthropic fetch wrapper to be configured\");\n }\n\n await wrapped(\"https://api.anthropic.com/v1/messages\", {\n method: \"POST\",\n body,\n });\n\n return mockFetch.mock.calls[0][1];\n };\n\n const wireBodyFor = async (\n requestBody: Record<string, unknown>,\n ): Promise<Record<string, unknown>> => {\n const init = await sendBody(JSON.stringify(requestBody));\n return JSON.parse(init.body as string);\n };\n\n it(\"requests summarized thinking for models that think by default\", async () => {\n for (const model of [\n \"claude-opus-5\",\n \"claude-sonnet-5\",\n \"claude-sonnet-5-20250929\",\n \"claude-fable-5\",\n ]) {\n const body = await wireBodyFor({ model, messages: [] });\n expect(body.thinking).toEqual({\n type: \"adaptive\",\n display: \"summarized\",\n });\n }\n });\n\n it(\"leaves models whose thinking is off by default untouched\", async () => {\n // Injecting thinking here would ENABLE it — new tokens, new cost —\n // rather than surface what already runs.\n const body = await wireBodyFor({\n model: \"claude-opus-4-8\",\n messages: [],\n });\n expect(body).not.toHaveProperty(\"thinking\");\n });\n\n it(\"respects an explicit thinking configuration\", async () => {\n const body = await wireBodyFor({\n model: \"claude-sonnet-5\",\n messages: [],\n thinking: { type: \"disabled\" },\n });\n expect(body.thinking).toEqual({ type: \"disabled\" });\n });\n\n it(\"passes a non-JSON body through untouched\", async () => {\n const init = await sendBody(\"not json\");\n expect(init.body).toBe(\"not json\");\n });\n });\n\n describe(\"azure fetchWithVersion\", () => {\n it(\"appends api-version to string URL\", async () => {\n const mockFetch = vi.fn().mockResolvedValue(new Response(\"{}\"));\n vi.stubGlobal(\"fetch\", mockFetch);\n\n createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"test-key\",\n modelName: \"gpt-4o\",\n baseUrl:\n \"https://my-resource.openai.azure.com/openai/deployments/gpt-4o\",\n });\n\n const fetchWithVersion = capturedCreateOpenAIOptions.fetch;\n expect(fetchWithVersion).toBeDefined();\n if (!fetchWithVersion) {\n throw new Error(\"Expected Azure fetchWithVersion to be configured\");\n }\n\n await fetchWithVersion(\n \"https://my-resource.openai.azure.com/openai/deployments/gpt-4o/chat/completions\",\n {},\n );\n\n expect(mockFetch).toHaveBeenCalledWith(\n expect.stringContaining(\"api-version=\"),\n expect.anything(),\n );\n\n vi.unstubAllGlobals();\n });\n\n it(\"appends api-version when input is a URL object\", async () => {\n const mockFetch = vi.fn().mockResolvedValue(new Response(\"{}\"));\n vi.stubGlobal(\"fetch\", mockFetch);\n\n createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"test-key\",\n modelName: \"gpt-4o\",\n baseUrl:\n \"https://my-resource.openai.azure.com/openai/deployments/gpt-4o\",\n });\n\n const fetchWithVersion = capturedCreateOpenAIOptions.fetch;\n expect(fetchWithVersion).toBeDefined();\n if (!fetchWithVersion) {\n throw new Error(\"Expected Azure fetchWithVersion to be configured\");\n }\n\n const urlObj = new URL(\n \"https://my-resource.openai.azure.com/openai/deployments/gpt-4o/chat/completions\",\n );\n await fetchWithVersion(urlObj, {});\n\n expect(mockFetch).toHaveBeenCalledWith(\n expect.stringContaining(\"api-version=\"),\n expect.anything(),\n );\n\n vi.unstubAllGlobals();\n });\n\n it(\"appends api-version when input is a Request object\", async () => {\n const mockFetch = vi.fn().mockResolvedValue(new Response(\"{}\"));\n vi.stubGlobal(\"fetch\", mockFetch);\n\n createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"test-key\",\n modelName: \"gpt-4o\",\n baseUrl:\n \"https://my-resource.openai.azure.com/openai/deployments/gpt-4o\",\n });\n\n const fetchWithVersion = capturedCreateOpenAIOptions.fetch;\n expect(fetchWithVersion).toBeDefined();\n if (!fetchWithVersion) {\n throw new Error(\"Expected Azure fetchWithVersion to be configured\");\n }\n\n const request = new Request(\n \"https://my-resource.openai.azure.com/openai/deployments/gpt-4o/chat/completions\",\n );\n await fetchWithVersion(request, {});\n\n expect(mockFetch).toHaveBeenCalledWith(\n expect.stringContaining(\"api-version=\"),\n expect.anything(),\n );\n\n vi.unstubAllGlobals();\n });\n\n it(\"uses globalThis.fetch when no provider fetch is configured\", async () => {\n const globalMockFetch = vi.fn().mockResolvedValue(new Response(\"{}\"));\n vi.stubGlobal(\"fetch\", globalMockFetch);\n\n createDirectLLMModel({\n provider: \"azure\",\n apiKey: \"test-key\",\n modelName: \"gpt-4o\",\n baseUrl:\n \"https://my-resource.openai.azure.com/openai/deployments/gpt-4o\",\n });\n\n const fetchWithVersion = capturedCreateOpenAIOptions.fetch;\n expect(fetchWithVersion).toBeDefined();\n if (!fetchWithVersion) {\n throw new Error(\"Expected Azure fetchWithVersion to be configured\");\n }\n\n await fetchWithVersion(\n \"https://my-resource.openai.azure.com/openai/deployments/gpt-4o/chat/completions\",\n {},\n );\n\n expect(globalMockFetch).toHaveBeenCalledWith(\n expect.stringContaining(\"api-version=\"),\n expect.anything(),\n );\n\n vi.unstubAllGlobals();\n });\n });\n\n it(\"throws descriptive error for cerebras provider without API key\", () => {\n expect(() =>\n createDirectLLMModel({\n provider: \"cerebras\",\n apiKey: undefined,\n modelName: \"llama-3.3-70b\",\n baseUrl: null,\n }),\n ).toThrow(\n \"Cerebras API key is required. Please configure CEREBRAS_API_KEY.\",\n );\n });\n\n it(\"throws descriptive error for cohere provider without API key\", () => {\n expect(() =>\n createDirectLLMModel({\n provider: \"cohere\",\n apiKey: undefined,\n modelName: \"command-light\",\n baseUrl: null,\n }),\n ).toThrow(\"Cohere API key is required. Please configure COHERE_API_KEY.\");\n });\n\n it(\"throws descriptive error for zhipuai provider without API key\", () => {\n expect(() =>\n createDirectLLMModel({\n provider: \"zhipuai\",\n apiKey: undefined,\n modelName: \"glm-4-flash\",\n baseUrl: null,\n }),\n ).toThrow(\n \"Zhipu AI API key is required. Please configure ZHIPUAI_API_KEY.\",\n );\n });\n});\n\ndescribe(\"createLLMModel\", () => {\n test(\"uses an explicit keyless Azure conversation key and forwards its inference URL to the proxy\", async ({\n makeOrganization,\n makeUser,\n makeSecret,\n makeAgent,\n }) => {\n mockIsAzureOpenAiEntraIdEnabled.mockReturnValue(true);\n\n const org = await makeOrganization();\n const user = await makeUser();\n const agent = await makeAgent({ name: \"Azure Chat Agent\", teams: [] });\n const fallbackSecret = await makeSecret({\n secret: { apiKey: \"sk-fallback\" },\n });\n\n const fallbackKey = await LlmProviderApiKeyModel.create({\n organizationId: org.id,\n secretId: fallbackSecret.id,\n name: \"Fallback Azure Key\",\n provider: \"azure\",\n scope: \"org\",\n baseUrl: \"https://fallback.example.com/openai\",\n inferenceBaseUrl: \"https://fallback-runtime.example.com/openai\",\n });\n const selectedKey = await LlmProviderApiKeyModel.create({\n organizationId: org.id,\n secretId: null,\n name: \"Selected Keyless Azure Key\",\n provider: \"azure\",\n scope: \"org\",\n baseUrl: \"https://discovery.example.com/openai\",\n inferenceBaseUrl: \"https://runtime.example.com/openai\",\n });\n const conversation = await ConversationModel.create({\n agentId: agent.id,\n userId: user.id,\n organizationId: org.id,\n title: \"Azure Chat Conversation\",\n chatApiKeyId: selectedKey.id,\n });\n\n const result = await createLLMModelForAgent({\n organizationId: org.id,\n userId: user.id,\n agentId: agent.id,\n model: \"gpt-4o\",\n provider: \"azure\",\n conversationId: conversation.id,\n source: \"chat\",\n });\n\n expect(result.apiKeySource).toBe(\"org\");\n expect(capturedCreateOpenAIOptions.apiKey).toBe(\"EMPTY\");\n expect(capturedCreateOpenAIOptions.headers).toEqual(\n expect.objectContaining({\n [CHAT_API_KEY_ID_HEADER]: selectedKey.id,\n [PROVIDER_BASE_URL_HEADER]: \"https://runtime.example.com/openai\",\n }),\n );\n expect(capturedCreateOpenAIOptions.headers).not.toEqual(\n expect.objectContaining({\n [CHAT_API_KEY_ID_HEADER]: fallbackKey.id,\n [PROVIDER_BASE_URL_HEADER]:\n \"https://fallback-runtime.example.com/openai\",\n }),\n );\n\n const originalFetch = globalThis.fetch;\n const fetchMock = vi.fn(async () => {\n return new Response(\n [\n 'data: {\"id\":\"chatcmpl-test\",\"object\":\"chat.completion.chunk\",\"created\":0,\"model\":\"gpt-4o\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"ok\"},\"finish_reason\":null}]}',\n 'data: {\"id\":\"chatcmpl-test\",\"object\":\"chat.completion.chunk\",\"created\":0,\"model\":\"gpt-4o\",\"choices\":[{\"index\":0,\"delta\":{},\"finish_reason\":\"stop\"}]}',\n \"data: [DONE]\",\n \"\",\n ].join(\"\\n\\n\"),\n {\n status: 200,\n headers: { \"content-type\": \"text/event-stream\" },\n },\n );\n });\n globalThis.fetch = fetchMock as typeof globalThis.fetch;\n\n try {\n const streamResult = streamText({\n model: result.model,\n prompt: \"hello\",\n });\n\n await expect(streamResult.text).resolves.toBe(\"ok\");\n } finally {\n globalThis.fetch = originalFetch;\n }\n\n expect(fetchMock).toHaveBeenCalled();\n const firstFetchCall = fetchMock.mock.calls[0] as unknown as Parameters<\n typeof globalThis.fetch\n >;\n const [, fetchInit] = firstFetchCall;\n expect(new Headers(fetchInit?.headers).get(\"authorization\")).toBe(\n \"Bearer EMPTY\",\n );\n });\n\n test(\"throws the typed connect prompt when the agent's key is another user's ChatGPT subscription\", async ({\n makeOrganization,\n makeUser,\n makeSecret,\n makeAgent,\n }) => {\n const org = await makeOrganization();\n const owner = await makeUser();\n const otherUser = await makeUser();\n const agent = await makeAgent({ name: \"Codex Agent\", teams: [] });\n const secret = await makeSecret({\n secret: {\n apiKey: encodeOpenAiCodexCredential({\n refreshToken: \"refresh-token\",\n accountId: \"account-id\",\n }),\n },\n });\n const ownerKey = await LlmProviderApiKeyModel.create({\n organizationId: org.id,\n secretId: secret.id,\n name: CHATGPT_SUBSCRIPTION_LABEL,\n provider: \"openai\",\n scope: \"personal\",\n userId: owner.id,\n });\n\n await expect(\n createLLMModelForAgent({\n organizationId: org.id,\n userId: otherUser.id,\n agentId: agent.id,\n model: \"gpt-5-codex\",\n provider: \"openai\",\n agentLlmApiKeyId: ownerKey.id,\n source: \"chat\",\n }),\n ).rejects.toMatchObject({\n name: \"LlmProviderAuthRequiredError\",\n provider: \"openai\",\n providerLabel: CHATGPT_SUBSCRIPTION_LABEL,\n });\n });\n\n test(\"sets the untrusted-context header only when contextIsTrusted is false\", () => {\n createLLMModel({\n provider: \"anthropic\",\n apiKey: \"test-key\",\n agentId: \"agent-1\",\n modelName: \"claude-3-5-haiku-20241022\",\n userId: \"user-1\",\n externalAgentId: \"external-agent-1\",\n sessionId: \"session-1\",\n source: \"chat\",\n baseUrl: null,\n contextIsTrusted: false,\n });\n\n expect(mockCreateAnthropic).toHaveBeenCalledWith(\n expect.objectContaining({\n headers: expect.objectContaining({\n [EXTERNAL_AGENT_ID_HEADER]: \"external-agent-1\",\n [USER_ID_HEADER]: \"user-1\",\n [SESSION_ID_HEADER]: \"session-1\",\n [SOURCE_HEADER]: \"chat\",\n [UNTRUSTED_CONTEXT_HEADER]: \"true\",\n }),\n }),\n );\n\n mockCreateAnthropic.mockClear();\n\n createLLMModel({\n provider: \"anthropic\",\n apiKey: \"test-key\",\n agentId: \"agent-1\",\n modelName: \"claude-3-5-haiku-20241022\",\n userId: \"user-1\",\n externalAgentId: \"external-agent-1\",\n sessionId: \"session-1\",\n source: \"chat\",\n baseUrl: null,\n contextIsTrusted: undefined,\n });\n\n expect(mockCreateAnthropic).toHaveBeenCalledWith(\n expect.objectContaining({\n headers: expect.not.objectContaining({\n [UNTRUSTED_CONTEXT_HEADER]: \"true\",\n }),\n }),\n );\n });\n\n for (const provider of [\"ollama\", \"ollama-native\", \"vllm\"] as const) {\n test(`${provider} resolves a chat model with no API key configured anywhere`, async ({\n makeOrganization,\n makeUser,\n makeAgent,\n }) => {\n const org = await makeOrganization();\n const user = await makeUser();\n const agent = await makeAgent({ name: `${provider} agent`, teams: [] });\n\n // The whole point of the self-hosted providers: no key exists, and\n // `resolveProviderApiKey` deliberately returns `apiKey: undefined` for\n // them. A hardcoded provider list in the guard drifts from that set —\n // which is how keyless `ollama-native` 400'd on every turn, the exact\n // setup the docs tell users to create.\n const result = await createLLMModelForAgent({\n organizationId: org.id,\n userId: user.id,\n agentId: agent.id,\n model: \"llama3.2\",\n provider,\n source: \"chat\",\n });\n\n expect(result.model).toBeDefined();\n });\n }\n});\n"}}}} |