#!/usr/bin/env node import { pathToFileURL } from 'url'; import { FastMCP } from 'fastmcp'; import { registerTools } from './tools'; const HTTP_TRANSPORT = 'httpStream'; const STDIO_TRANSPORT = 'stdio'; const DEFAULT_HTTP_HOST = '127.0.0.1'; const DEFAULT_HTTP_ENDPOINT = '/mcp'; const MCP_HTTP_IDENTITY_SERVICE = 'agent-teams-mcp-http'; const MCP_HTTP_IDENTITY_SERVICE_ENV = 'AGENT_TEAMS_MCP_HTTP_IDENTITY_SERVICE'; const MCP_HTTP_CLAUDE_DIR_HASH_ENV = 'AGENT_TEAMS_MCP_HTTP_CLAUDE_DIR_HASH'; const MCP_HTTP_LAUNCH_SPEC_HASH_ENV = 'AGENT_TEAMS_MCP_HTTP_LAUNCH_SPEC_HASH'; const MCP_HTTP_OWNER_INSTANCE_ID_ENV = 'AGENT_TEAMS_MCP_HTTP_OWNER_INSTANCE_ID'; export type AgentTeamsMcpStartOptions = | { transportType: typeof STDIO_TRANSPORT; } | { transportType: typeof HTTP_TRANSPORT; httpStream: { host: string; port: number; endpoint: `/${string}`; }; }; export interface AgentTeamsMcpHttpHealthIdentity { schemaVersion: 1; service: typeof MCP_HTTP_IDENTITY_SERVICE; transport: typeof HTTP_TRANSPORT; host: string; port: number; endpoint: `/${string}`; claudeDirHash: string; launchSpecHash: string; ownerInstanceId: string; } export function createServer(input: { healthIdentity?: AgentTeamsMcpHttpHealthIdentity | null } = {}) { const server = new FastMCP({ name: 'agent-teams-mcp', version: '1.0.0', ...(input.healthIdentity ? { health: { enabled: true, path: '/health', status: 200, message: JSON.stringify(input.healthIdentity), }, } : {}), }); registerTools(server); return server; } function getArgValue(argv: string[], name: string): string | null { const directPrefix = `${name}=`; for (let index = 2; index < argv.length; index += 1) { const value = argv[index]; if (value === name) { return argv[index + 1] ?? null; } if (value.startsWith(directPrefix)) { return value.slice(directPrefix.length); } } return null; } function normalizeEndpoint(value: string | null | undefined): `/${string}` { const trimmed = value?.trim(); if (!trimmed) { return DEFAULT_HTTP_ENDPOINT; } return (trimmed.startsWith('/') ? trimmed : `/${trimmed}`) as `/${string}`; } function parsePort(value: string | null | undefined): number { const parsed = Number(value); if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) { throw new Error(`Invalid agent-teams MCP HTTP port: ${value ?? ''}`); } return parsed; } function readIdentityValue(env: NodeJS.ProcessEnv, name: string): string | null { const value = env[name]?.trim(); return value && value.length > 0 ? value : null; } export function buildHttpHealthIdentity( options: AgentTeamsMcpStartOptions, env: NodeJS.ProcessEnv = process.env ): AgentTeamsMcpHttpHealthIdentity | null { if (options.transportType !== HTTP_TRANSPORT) { return null; } const service = readIdentityValue(env, MCP_HTTP_IDENTITY_SERVICE_ENV); const claudeDirHash = readIdentityValue(env, MCP_HTTP_CLAUDE_DIR_HASH_ENV); const launchSpecHash = readIdentityValue(env, MCP_HTTP_LAUNCH_SPEC_HASH_ENV); const ownerInstanceId = readIdentityValue(env, MCP_HTTP_OWNER_INSTANCE_ID_ENV); if ( service !== MCP_HTTP_IDENTITY_SERVICE || !claudeDirHash || !launchSpecHash || !ownerInstanceId ) { return null; } return { schemaVersion: 1, service: MCP_HTTP_IDENTITY_SERVICE, transport: HTTP_TRANSPORT, host: options.httpStream.host, port: options.httpStream.port, endpoint: options.httpStream.endpoint, claudeDirHash, launchSpecHash, ownerInstanceId, }; } export function resolveStartOptions( argv: string[] = process.argv, env: NodeJS.ProcessEnv = process.env ): AgentTeamsMcpStartOptions { const transport = getArgValue(argv, '--transport') ?? getArgValue(argv, '--transportType') ?? env.AGENT_TEAMS_MCP_TRANSPORT ?? STDIO_TRANSPORT; if (transport !== HTTP_TRANSPORT) { return { transportType: STDIO_TRANSPORT }; } return { transportType: HTTP_TRANSPORT, httpStream: { host: getArgValue(argv, '--host')?.trim() ?? env.AGENT_TEAMS_MCP_HTTP_HOST?.trim() ?? DEFAULT_HTTP_HOST, port: parsePort(getArgValue(argv, '--port') ?? env.AGENT_TEAMS_MCP_HTTP_PORT), endpoint: normalizeEndpoint(getArgValue(argv, '--endpoint') ?? env.AGENT_TEAMS_MCP_HTTP_ENDPOINT), }, }; } if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { const startOptions = resolveStartOptions(); const server = createServer({ healthIdentity: buildHttpHealthIdentity(startOptions) }); void server.start(startOptions); }