diff --git a/.env.integrated b/.env.integrated new file mode 100644 index 0000000..aca7862 --- /dev/null +++ b/.env.integrated @@ -0,0 +1,4 @@ +AUTH_MODE=integrated +BASE_URI=http://localhost:3232 +PORT=3232 +REDIS_URL=redis://localhost:6379 \ No newline at end of file diff --git a/.env.separate b/.env.separate new file mode 100644 index 0000000..e0c6565 --- /dev/null +++ b/.env.separate @@ -0,0 +1,6 @@ +AUTH_MODE=separate +BASE_URI=http://localhost:3232 +PORT=3232 +REDIS_URL=redis://localhost:6379 +AUTH_SERVER_URL=http://localhost:3001 +AUTH_SERVER_PORT=3001 \ No newline at end of file diff --git a/.gitignore b/.gitignore index dabd58e..133e221 100644 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,6 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* + +# Claude config directory +.claude/ diff --git a/src/config.ts b/src/config.ts index c3a29f7..cffc9c9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,50 @@ import "dotenv/config"; +/** + * Port for the MCP server to listen on + */ export const PORT = Number(process.env.PORT) || 3232; -export const BASE_URI = - process.env.BASE_URI || "https://localhost:3232"; +/** + * Base URI for the MCP server. Used for OAuth callbacks and metadata. + * Should match the port if specified separately. + */ +export const BASE_URI = process.env.BASE_URI || `http://localhost:${PORT}`; + +// Validate PORT and BASE_URI consistency +const baseUrl = new URL(BASE_URI); +if (baseUrl.port && parseInt(baseUrl.port) !== PORT) { + console.warn(`Warning: BASE_URI port (${baseUrl.port}) doesn't match PORT (${PORT})`); +} + +/** + * Redis connection URL + */ +export const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379'; + +/** + * Authentication mode: + * - 'integrated': MCP server acts as its own OAuth server (default) + * - 'separate': MCP server delegates to external auth server + */ +export const AUTH_MODE = (process.env.AUTH_MODE as 'integrated' | 'separate') || 'integrated'; + +/** + * Port for the standalone auth server (only used in separate mode) + * Used when running the auth-server component + */ +export const AUTH_SERVER_PORT = parseInt(process.env.AUTH_SERVER_PORT || '3001'); + +/** + * URL of the external authorization server (only used when AUTH_MODE='separate') + * This is where the MCP server will redirect clients for authentication + */ +export const AUTH_SERVER_URL = process.env.AUTH_SERVER_URL || `http://localhost:${AUTH_SERVER_PORT}`; + +// Validate AUTH_SERVER configuration +if (AUTH_MODE === 'separate') { + const authUrl = new URL(AUTH_SERVER_URL); + if (authUrl.port && parseInt(authUrl.port) !== AUTH_SERVER_PORT) { + throw new Error(`Configuration error: AUTH_SERVER_URL port (${authUrl.port}) doesn't match AUTH_SERVER_PORT (${AUTH_SERVER_PORT})`); + } +} diff --git a/src/redis.ts b/src/redis.ts index 1826bff..3fc1a47 100644 --- a/src/redis.ts +++ b/src/redis.ts @@ -1,5 +1,6 @@ import { createClient, SetOptions } from "@redis/client"; import { logger } from "./utils/logger.js"; +import { REDIS_URL } from "./config.js"; /** * Describes the Redis primitives we use in this application, to be able to mock @@ -37,7 +38,7 @@ export interface RedisClient { export class RedisClientImpl implements RedisClient { private redis = createClient({ - url: process.env.REDIS_URL || "redis://localhost:6379", + url: REDIS_URL, password: process.env.REDIS_PASSWORD, socket: { tls: process.env.REDIS_TLS === "1", @@ -97,7 +98,7 @@ export class RedisClientImpl implements RedisClient { } get options() { - return { url: process.env.REDIS_URL || "redis://localhost:6379" }; + return { url: REDIS_URL }; } async createSubscription(