diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3eac763..00c030b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: branches: [main] env: - BUN_VERSION: "1.3.9" + BUN_VERSION: "1.3.13" jobs: check: @@ -31,13 +31,6 @@ jobs: - name: Build run: bun build --compile src/cli/index.ts --outfile dist/mcp2cli - - name: Upload binary - uses: actions/upload-artifact@v4 - with: - name: mcp2cli-binary - path: dist/mcp2cli - retention-days: 7 - deploy: needs: check runs-on: [self-hosted, rodaddy] diff --git a/README.md b/README.md index d94650d..4e3d27c 100644 --- a/README.md +++ b/README.md @@ -630,8 +630,8 @@ In addition to the [base environment variables](#environment-variables), network |----------|---------|-------------| | `MCP2CLI_LISTEN_HOST` | (unset) | Bind address for TCP mode. Setting this enables TCP instead of Unix socket. Use `0.0.0.0` to listen on all interfaces | | `MCP2CLI_LISTEN_PORT` | `9500` | TCP port when `MCP2CLI_LISTEN_HOST` is set | -| `MCP2CLI_AUTH_TOKEN` | (unset) | Bearer token for TCP authentication. Required for production deployments | -| `MCP2CLI_REMOTE_URL` | (unset) | URL of remote mcp2cli daemon (e.g. `http://mcp-server:9500`). Enables remote client mode | +| `MCP2CLI_AUTH_TOKEN` | (unset) | Bearer token for TCP authentication. Required for production deployments. Alias: `MCP_TOKEN` | +| `MCP2CLI_REMOTE_URL` | (unset) | URL of remote mcp2cli daemon (e.g. `https://mcp2cli.rodaddy.live`). Enables remote client mode. Alias: `MCP_HOST` | | `MCP2CLI_CONFIG` | `~/.config/mcp2cli/services.json` | Path to service definitions (useful for server-side config in `/etc/mcp2cli/`) | | `MCP2CLI_TOKENS_FILE` | `~/.config/mcp2cli/tokens.json` | Path to multi-user token/RBAC config | | `MCP2CLI_CREDENTIALS_FILE` | `~/.config/mcp2cli/credentials.json` | Path to per-identity credential mappings | diff --git a/src/cli/commands/cache.ts b/src/cli/commands/cache.ts index ac6d89a..fc131be 100644 --- a/src/cli/commands/cache.ts +++ b/src/cli/commands/cache.ts @@ -4,7 +4,8 @@ */ import { clearCache, listCachedServices, readCacheRaw, detectDrift, resolveTtlMs, writeCache, mapToolsToCachedSchemas } from "../../cache/index.ts"; import { loadConfig } from "../../config/index.ts"; -import { connectToService, connectToHttpService } from "../../connection/index.ts"; +import { connectToService } from "../../connection/index.ts"; +import { connectToHttpService } from "../../connection/http-transport.ts"; import { connectToWebSocketService } from "../../connection/websocket-transport.ts"; import { listAllTools } from "../../schema/introspect.ts"; import { EXIT_CODES } from "../../types/index.ts"; diff --git a/src/cli/commands/schema.ts b/src/cli/commands/schema.ts index 862f14f..ada3866 100644 --- a/src/cli/commands/schema.ts +++ b/src/cli/commands/schema.ts @@ -5,7 +5,8 @@ * Supports --fresh flag to bypass cache for one call. */ import { loadConfig } from "../../config/index.ts"; -import { connectToService, connectToHttpService } from "../../connection/index.ts"; +import { connectToService } from "../../connection/index.ts"; +import { connectToHttpService } from "../../connection/http-transport.ts"; import { connectToWebSocketService } from "../../connection/websocket-transport.ts"; import { getSchemaViaDaemon } from "../../process/index.ts"; import { diff --git a/src/cli/commands/service-help.ts b/src/cli/commands/service-help.ts index 2b6ac3a..6ca33eb 100644 --- a/src/cli/commands/service-help.ts +++ b/src/cli/commands/service-help.ts @@ -3,7 +3,8 @@ * Routes through daemon by default. Set MCP2CLI_NO_DAEMON=1 for direct connection. */ import { loadConfig } from "../../config/index.ts"; -import { connectToService, connectToHttpService } from "../../connection/index.ts"; +import { connectToService } from "../../connection/index.ts"; +import { connectToHttpService } from "../../connection/http-transport.ts"; import { connectToWebSocketService } from "../../connection/websocket-transport.ts"; import { listToolsViaDaemon } from "../../process/index.ts"; import { listToolsCached, formatToolListing } from "../../schema/index.ts"; diff --git a/src/cli/commands/tool-call.ts b/src/cli/commands/tool-call.ts index da163e9..b208532 100644 --- a/src/cli/commands/tool-call.ts +++ b/src/cli/commands/tool-call.ts @@ -7,7 +7,8 @@ import { } from "../../invocation/index.ts"; import { validationResultToCliError } from "../../validation/pipelines.ts"; import { loadConfig } from "../../config/index.ts"; -import { connectToService, connectToHttpService } from "../../connection/index.ts"; +import { connectToService } from "../../connection/index.ts"; +import { connectToHttpService } from "../../connection/http-transport.ts"; import { connectToWebSocketService } from "../../connection/websocket-transport.ts"; import { callViaDaemon, getSchemaViaDaemon } from "../../process/index.ts"; import { getToolSchemaCached, resolveToolNameCached } from "../../schema/cached.ts"; diff --git a/src/connection/index.ts b/src/connection/index.ts index 68008c2..9ab7df6 100644 --- a/src/connection/index.ts +++ b/src/connection/index.ts @@ -2,5 +2,4 @@ export { ConnectionError } from "./errors.ts"; export { isJsonRpcLine } from "./filter.ts"; export { McpTransport } from "./transport.ts"; export { connectToService } from "./client.ts"; -export { connectToHttpService } from "./http-transport.ts"; export type { ConnectionOptions, McpConnection } from "./types.ts"; diff --git a/src/daemon/auth-provider.ts b/src/daemon/auth-provider.ts index 0f230c5..ddca65c 100644 --- a/src/daemon/auth-provider.ts +++ b/src/daemon/auth-provider.ts @@ -221,7 +221,7 @@ export class TokenAuthProvider implements AuthProvider { } // Fall back to legacy single-token env var - const envToken = process.env.MCP2CLI_AUTH_TOKEN; + const envToken = process.env.MCP2CLI_AUTH_TOKEN ?? process.env.MCP_TOKEN; if (envToken) { log.info("using_legacy_token", { source: "MCP2CLI_AUTH_TOKEN" }); return TokenAuthProvider.fromEnvToken(envToken); diff --git a/src/daemon/auth.ts b/src/daemon/auth.ts index 1a6547d..f9e6bc3 100644 --- a/src/daemon/auth.ts +++ b/src/daemon/auth.ts @@ -17,7 +17,7 @@ const AUTH_EXEMPT_PATHS = new Set(["/health", "/metrics", "/", "/api/auth/login" * @deprecated Use TokenAuthProvider.load() for multi-user support. */ export function loadAuthToken(): string | undefined { - return process.env.MCP2CLI_AUTH_TOKEN || undefined; + return process.env.MCP2CLI_AUTH_TOKEN || process.env.MCP_TOKEN || undefined; } /** diff --git a/src/daemon/paths.ts b/src/daemon/paths.ts index 820f2ee..7920d35 100644 --- a/src/daemon/paths.ts +++ b/src/daemon/paths.ts @@ -44,9 +44,9 @@ export function getDaemonListenConfig(): DaemonListenConfig { * Returns null if MCP2CLI_REMOTE_URL is not set. */ export function getRemoteConfig(): { url: string; token: string | undefined } | null { - const url = process.env.MCP2CLI_REMOTE_URL; + const url = process.env.MCP2CLI_REMOTE_URL ?? process.env.MCP_HOST; if (!url) return null; - return { url, token: process.env.MCP2CLI_AUTH_TOKEN }; + return { url, token: process.env.MCP2CLI_AUTH_TOKEN ?? process.env.MCP_TOKEN }; } export function getDaemonPaths(overrides?: { diff --git a/src/daemon/pool.ts b/src/daemon/pool.ts index a5042ed..7262cc1 100644 --- a/src/daemon/pool.ts +++ b/src/daemon/pool.ts @@ -4,7 +4,8 @@ * MEM-04: Bounded pool size (default 50, configurable via MCP2CLI_POOL_MAX). * MEM-05: Health check before reuse -- stale connections are replaced. */ -import { connectToService, connectToHttpService } from "../connection/index.ts"; +import { connectToService } from "../connection/index.ts"; +import { connectToHttpService } from "../connection/http-transport.ts"; import { connectToWebSocketService } from "../connection/websocket-transport.ts"; import { ConnectionError } from "../connection/errors.ts"; import type { McpConnection } from "../connection/types.ts"; diff --git a/src/process/client.ts b/src/process/client.ts index f94d451..12282fc 100644 --- a/src/process/client.ts +++ b/src/process/client.ts @@ -42,8 +42,8 @@ async function getLocalToken(): Promise { // Only use MCP2CLI_AUTH_TOKEN for local auth when no remote URL is set, // otherwise the token belongs to the remote daemon - const envToken = process.env.MCP2CLI_AUTH_TOKEN; - if (envToken && !process.env.MCP2CLI_REMOTE_URL) { + const envToken = process.env.MCP2CLI_AUTH_TOKEN ?? process.env.MCP_TOKEN; + if (envToken && !process.env.MCP2CLI_REMOTE_URL && !process.env.MCP_HOST) { cachedLocalToken = envToken; return cachedLocalToken; } diff --git a/tests/connection/http-transport.test.ts b/tests/connection/http-transport.test.ts index d275612..e96fb02 100644 --- a/tests/connection/http-transport.test.ts +++ b/tests/connection/http-transport.test.ts @@ -146,12 +146,7 @@ describe("ServicesConfig with mixed backends", () => { }); describe("HTTP transport module imports", () => { - test("connectToHttpService is exported from connection index", async () => { - const mod = await import("../../src/connection/index.ts"); - expect(typeof mod.connectToHttpService).toBe("function"); - }); - - test("connectToHttpService function exists in http-transport module", async () => { + test("connectToHttpService is exported from http-transport module", async () => { const mod = await import("../../src/connection/http-transport.ts"); expect(typeof mod.connectToHttpService).toBe("function"); }); diff --git a/tests/daemon/pool.test.ts b/tests/daemon/pool.test.ts index 8d43027..47027e0 100644 --- a/tests/daemon/pool.test.ts +++ b/tests/daemon/pool.test.ts @@ -17,6 +17,9 @@ const mockConnectToService = mock(async () => { // Apply the mock to the connection module mock.module("../../src/connection/index.ts", () => ({ connectToService: mockConnectToService, +})); + +mock.module("../../src/connection/http-transport.ts", () => ({ connectToHttpService: mockConnectToService, })); diff --git a/tests/resilience/pool-fallback.test.ts b/tests/resilience/pool-fallback.test.ts index af8e0e3..3c5c571 100644 --- a/tests/resilience/pool-fallback.test.ts +++ b/tests/resilience/pool-fallback.test.ts @@ -29,10 +29,13 @@ const mockConnectToHttpService = mock(async () => mockHttpConnection); const mockConnectToService = mock(async () => mockStdioConnection); mock.module("../../src/connection/index.ts", () => ({ - connectToHttpService: mockConnectToHttpService, connectToService: mockConnectToService, })); +mock.module("../../src/connection/http-transport.ts", () => ({ + connectToHttpService: mockConnectToHttpService, +})); + // Import pool AFTER mocking const { ConnectionPool } = await import("../../src/daemon/pool.ts");