@fruggr/zendesk-mcp-server 1.9.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1384,10 +1384,10 @@ const createHelpCenterTools = (ctx) => {
1384
1384
  },
1385
1385
  handler: async (params) => {
1386
1386
  const { name } = params;
1387
- const { record } = await zendeskPost(subdomain, await getToken(), "/guide/content_tags", { record: { name } });
1387
+ const { content_tag } = await zendeskPost(subdomain, await getToken(), "/guide/content_tags", { content_tag: { name } });
1388
1388
  return { content: [{
1389
1389
  type: "text",
1390
- text: `Content tag created.\n\n${formatContentTag(record)}`
1390
+ text: `Content tag created.\n\n${formatContentTag(content_tag)}`
1391
1391
  }] };
1392
1392
  }
1393
1393
  },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/auth/api-token.ts","../src/utils/logger.ts","../src/constants.ts","../src/auth/browser-oauth.ts","../src/utils/package-info.ts","../src/auth/token-persistence.ts","../src/auth/token-store.ts","../src/config.ts","../src/client/zendesk-api.ts","../src/routing/registry.ts","../src/utils/article-sections.ts","../src/utils/formatting.ts","../src/utils/pagination.ts","../src/tools/help-center.ts","../src/tools/search.ts","../src/tools/tickets.ts","../src/tools/users.ts","../src/tools/index.ts","../src/server.ts","../src/transports/http.ts","../src/transports/stdio.ts","../src/index.ts"],"sourcesContent":["/**\n * API token authentication for stdio transport.\n * Uses Basic auth: base64(email/token:api_token)\n */\nexport const buildBasicAuthHeader = (email: string, apiToken: string): string => {\n const credentials = `${email}/token:${apiToken}`;\n return `Basic ${Buffer.from(credentials).toString('base64')}`;\n};\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { LogLevel } from '../config';\n\ntype Fields = Record<string, unknown>;\n\n/**\n * Minimal structured logger with two sinks:\n * - stderr (always, gated by the configured level) — the universal floor that\n * every mainstream MCP client captures to a log file;\n * - MCP `notifications/message` (when a server is attached and advertises the\n * `logging` capability) — surfaced by clients that support it, ignored\n * gracefully by those that don't.\n *\n * Both sinks share a single level gate (`LOG_LEVEL`); MCP clients may further\n * raise the floor via `logging/setLevel`, which the SDK filters on its own.\n */\nexport interface Logger {\n debug(event: string, fields?: Fields): void;\n info(event: string, fields?: Fields): void;\n warn(event: string, fields?: Fields): void;\n error(event: string, fields?: Fields): void;\n /** Attach an McpServer so logs are also emitted as MCP notifications. */\n attachServer(server: Pick<McpServer, 'sendLoggingMessage'>): void;\n}\n\nconst SEVERITY: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 };\n\n// Our 4 levels mapped onto the MCP logging levels (note: `warning`, not `warn`).\nconst MCP_LEVEL: Record<LogLevel, 'debug' | 'info' | 'warning' | 'error'> = {\n debug: 'debug',\n info: 'info',\n warn: 'warning',\n error: 'error',\n};\n\n// Field keys whose values must never reach a sink, matched after normalising\n// (lowercase, separators stripped) so `access_token`, `accessToken` and\n// `ACCESS-TOKEN` all collapse to the same key. Diagnostic fields such as\n// `errorCode`, `status` or `error` (a message) are intentionally NOT listed.\nconst REDACTED_KEYS = new Set([\n 'token',\n 'accesstoken',\n 'refreshtoken',\n 'code',\n 'codeverifier',\n 'authorization',\n 'password',\n 'secret',\n 'apitoken',\n 'bearer',\n]);\n\nconst isSensitive = (key: string): boolean =>\n REDACTED_KEYS.has(key.toLowerCase().replace(/[_-]/g, ''));\n\n// Recursively redact: a sensitive *key* anywhere in the tree (top-level or\n// nested in objects/arrays) has its value replaced, so `{ oauth: { token } }`\n// can't leak. Primitives are returned as-is.\nconst redactValue = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(redactValue);\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n out[key] = isSensitive(key) ? '[REDACTED]' : redactValue(val);\n }\n return out;\n }\n return value;\n};\n\nconst renderValue = (value: unknown): string => {\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean' || value === null) {\n return String(value);\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '[unserializable]';\n }\n};\n\nconst formatLine = (level: LogLevel, event: string, fields: Fields): string => {\n const parts = Object.entries(fields).map(([key, value]) => `${key}=${renderValue(value)}`);\n return `[zendesk-mcp] [${level}] ${event}${parts.length ? ` ${parts.join(' ')}` : ''}`;\n};\n\nconst noop = (): void => {};\n\nexport const silentLogger: Logger = {\n debug: noop,\n info: noop,\n warn: noop,\n error: noop,\n attachServer: noop,\n};\n\nexport const createLogger = (level: LogLevel): Logger => {\n const min = SEVERITY[level];\n let server: Pick<McpServer, 'sendLoggingMessage'> | undefined;\n\n const emit = (lvl: LogLevel, event: string, fields?: Fields): void => {\n if (SEVERITY[lvl] < min) return;\n\n const safe = fields ? (redactValue(fields) as Fields) : {};\n console.error(formatLine(lvl, event, safe));\n\n if (server) {\n try {\n void server\n .sendLoggingMessage({\n level: MCP_LEVEL[lvl],\n logger: 'zendesk-mcp-server',\n // `event` last so a caller-supplied `fields.event` can't shadow the\n // canonical event name.\n data: { ...safe, event },\n })\n .catch(noop);\n } catch {\n // Forwarding is best-effort (e.g. transport not connected yet);\n // it must never break the flow that emitted the log.\n }\n }\n };\n\n return {\n debug: (event, fields) => emit('debug', event, fields),\n info: (event, fields) => emit('info', event, fields),\n warn: (event, fields) => emit('warn', event, fields),\n error: (event, fields) => emit('error', event, fields),\n attachServer: (s) => {\n server = s;\n },\n };\n};\n","export const CHARACTER_LIMIT = 25_000;\nexport const DEFAULT_PAGE_SIZE = 100;\nexport const MAX_PAGE_SIZE = 100;\nexport const TOKEN_CACHE_TTL_MS = 5 * 60 * 1000;\n\n// Local port the OAuth PKCE flow listens on for the browser callback. Must match\n// the redirect URL registered in the Zendesk OAuth client. Deliberately picked\n// outside the usual dev range (3000/5000/8080…) and below the OS ephemeral\n// ranges (Linux ≥ 32768, Windows ≥ 49152) so it is neither commonly taken nor\n// grabbed by a transient socket. Override via ZENDESK_OAUTH_CALLBACK_PORT /\n// --callback-port (and register the matching redirect URL in Zendesk).\nexport const DEFAULT_CALLBACK_PORT = 27439;\n\n// Per-attachment cap for inline image content. Images larger than this are\n// returned as text references instead of base64 image content blocks.\n// Aligned with the Anthropic vision API per-image limit.\nexport const MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;\n\n// Maximum number of images embedded as base64 in a single tool call.\n// Remaining images are returned as text references.\nexport const MAX_EMBEDDED_IMAGE_COUNT = 10;\n\n// Hard cap on comment pages fetched when collecting ticket attachments.\n// Overridable via ZENDESK_MAX_COMMENT_PAGES for tickets with many comments.\nexport const MAX_COMMENT_PAGES = Number(process.env['ZENDESK_MAX_COMMENT_PAGES'] ?? 10);\n\n// Thresholds used to nudge callers toward section-scoped article tools\n// (get_article_outline / get_article_section / update_article_section)\n// instead of fetching/rewriting the full body.\nexport const LARGE_ARTICLE_BODY_CHARS = 3_000;\nexport const LARGE_ARTICLE_SECTION_COUNT = 4;\n\nexport const getBaseUrl = (subdomain: string): string => `https://${subdomain}.zendesk.com/api/v2`;\n\nexport const getHelpCenterBaseUrl = (subdomain: string): string =>\n `https://${subdomain}.zendesk.com/api/v2/help_center`;\n\nexport const getOAuthUrls = (subdomain: string) => ({\n authorizeUrl: `https://${subdomain}.zendesk.com/oauth/authorizations/new`,\n tokenUrl: `https://${subdomain}.zendesk.com/oauth/tokens`,\n});\n","import { createHash, randomBytes } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nimport { createServer, type Server } from 'node:http';\nimport { release } from 'node:os';\nimport open from 'open';\nimport { DEFAULT_CALLBACK_PORT, getOAuthUrls } from '../constants';\nimport { type Logger, silentLogger } from '../utils/logger';\n\nconst AUTH_TIMEOUT_MS = 5 * 60 * 1000;\n\n/** Best-effort WSL detection: WSL kernels carry \"microsoft\" in /proc/version. */\nconst detectWsl = (): boolean => {\n if (process.platform !== 'linux') return false;\n try {\n return readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft');\n } catch {\n return false;\n }\n};\n\ninterface BrowserOAuthConfig {\n subdomain: string;\n oauthClientId: string;\n callbackPort?: number | undefined;\n}\n\ninterface TokenResult {\n access_token: string;\n refresh_token?: string;\n token_type: string;\n scope: string;\n // Present only when the Zendesk OAuth client has token expiration enabled.\n // Seconds until the access/refresh token expires.\n expires_in?: number;\n refresh_token_expires_in?: number;\n}\n\n/**\n * Build an actionable error for a callback port that's already taken. The raw\n * Node `EADDRINUSE` is opaque to both the user and the LLM; this spells out the\n * fix (set a free port + register the matching redirect URL in Zendesk). The\n * `(EADDRINUSE)` marker and `code` are kept for diagnostics/tests.\n */\nconst callbackPortInUseError = (port: number, cause: Error): Error =>\n Object.assign(\n new Error(\n `Cannot start the Zendesk OAuth sign-in: local callback port ${port} is already in use ` +\n `by another process. Set ZENDESK_OAUTH_CALLBACK_PORT (or --callback-port) to a free port, ` +\n `then register http://localhost:<port>/callback as a redirect URL in your Zendesk OAuth ` +\n `client. (EADDRINUSE)`,\n ),\n { code: 'EADDRINUSE', cause },\n );\n\n/**\n * Escape a string for safe interpolation into HTML text/attribute context.\n * The local callback server echoes attacker-controllable values (the OAuth\n * `error_description` query param, token-exchange error bodies) back into the\n * browser response; without escaping these are a reflected-XSS sink.\n */\nconst escapeHtml = (value: string): string =>\n value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n\n/**\n * A started OAuth flow: the local callback server is already listening and the\n * browser-open attempt has been made. `authorizeUrl` is available immediately\n * (so callers can surface it to the user without blocking), while `tokenPromise`\n * resolves later, when the user completes the browser flow — or rejects on\n * error/timeout.\n */\nexport interface StartedBrowserAuth {\n authorizeUrl: string;\n tokenPromise: Promise<TokenResult>;\n}\n\nconst generateCodeVerifier = (): string => randomBytes(32).toString('base64url');\n\nconst generateCodeChallenge = (verifier: string): string =>\n createHash('sha256').update(verifier).digest('base64url');\n\n/**\n * Begin the OAuth 2.1 PKCE flow: start the local callback server, attempt to\n * open the browser, and return as soon as the server is listening with the\n * authorize URL plus a promise that settles when the callback arrives.\n *\n * Splitting \"start\" from \"await the token\" lets callers stay non-blocking: they\n * can hand the URL back to the user immediately instead of holding a request\n * open for up to the 5-minute timeout.\n */\nexport const startBrowserAuth = (\n config: BrowserOAuthConfig,\n logger: Logger = silentLogger,\n): Promise<StartedBrowserAuth> => {\n const { subdomain, oauthClientId } = config;\n const { authorizeUrl: authorizeBase, tokenUrl } = getOAuthUrls(subdomain);\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n\n return new Promise<StartedBrowserAuth>((resolveStarted, rejectStarted) => {\n let resolveToken!: (token: TokenResult) => void;\n let rejectToken!: (err: unknown) => void;\n const tokenPromise = new Promise<TokenResult>((resolve, reject) => {\n resolveToken = resolve;\n rejectToken = reject;\n });\n\n let authTimeout: ReturnType<typeof setTimeout> | undefined;\n let callbackServer: Server;\n\n callbackServer = createServer(async (req, res) => {\n const url = new URL(req.url ?? '/', `http://localhost`);\n\n if (url.pathname !== '/callback') {\n res.writeHead(404);\n res.end('Not found');\n return;\n }\n\n const code = url.searchParams.get('code');\n const error = url.searchParams.get('error');\n\n logger.debug('oauth_callback_received', {\n hasCode: Boolean(code),\n hasError: Boolean(error),\n });\n\n if (error) {\n const desc = url.searchParams.get('error_description') ?? error;\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(\n `<html><body><h1>Authentication failed</h1><p>${escapeHtml(desc)}</p></body></html>`,\n );\n clearTimeout(authTimeout);\n callbackServer.close();\n rejectToken(new Error(`OAuth error: ${desc}`));\n return;\n }\n\n if (!code) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end('<html><body><h1>Missing authorization code</h1></body></html>');\n clearTimeout(authTimeout);\n callbackServer.close();\n rejectToken(new Error('Missing authorization code in callback'));\n return;\n }\n\n // Exchange code for token\n try {\n const callbackPort = (callbackServer.address() as { port: number }).port;\n const tokenBody = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n client_id: oauthClientId,\n redirect_uri: `http://localhost:${callbackPort}/callback`,\n code_verifier: codeVerifier,\n });\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: tokenBody.toString(),\n });\n\n logger.debug('oauth_token_exchange', { status: tokenResponse.status });\n\n if (!tokenResponse.ok) {\n const errorBody = await tokenResponse.text();\n throw new Error(`Token exchange failed (${tokenResponse.status}): ${errorBody}`);\n }\n\n const tokenData = (await tokenResponse.json()) as TokenResult;\n logger.info('oauth_authenticated');\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n '<html><body>' +\n '<h1>Authentication successful!</h1>' +\n '<p>You can close this tab and return to Claude Code.</p>' +\n '<p>This tab will auto-close in <span id=\"t\">10</span>s.</p>' +\n '<script>' +\n 'let n=10;' +\n 'const el=document.getElementById(\"t\");' +\n 'const i=setInterval(()=>{n--;el.textContent=n;if(n<=0){clearInterval(i);window.close();}},1000);' +\n '</script>' +\n '</body></html>',\n );\n\n clearTimeout(authTimeout);\n callbackServer.close();\n resolveToken(tokenData);\n } catch (err) {\n res.writeHead(500, { 'Content-Type': 'text/html' });\n res.end(\n `<html><body><h1>Token exchange failed</h1><p>${escapeHtml(err instanceof Error ? err.message : String(err))}</p></body></html>`,\n );\n clearTimeout(authTimeout);\n callbackServer.close();\n rejectToken(err);\n }\n });\n\n const requestedPort = config.callbackPort ?? DEFAULT_CALLBACK_PORT;\n\n // Listen failure (e.g. port already in use) before we ever get a URL: the\n // whole start fails so the caller can surface/retry. EADDRINUSE is rewrapped\n // into an actionable message (user *and* LLM can act on it).\n const onStartError = (err: Error) => {\n clearTimeout(authTimeout);\n const code = (err as NodeJS.ErrnoException).code;\n logger.error('oauth_callback_listen_failed', { port: requestedPort, errorCode: code });\n rejectStarted(code === 'EADDRINUSE' ? callbackPortInUseError(requestedPort, err) : err);\n };\n callbackServer.once('error', onStartError);\n\n // Start on fixed port (must match redirect_uri registered in Zendesk OAuth client)\n callbackServer.listen(requestedPort, () => {\n // Now that we're listening, a later server error must settle the *token*\n // flow (the started promise is already resolved) and tear the server down,\n // so the token store doesn't wedge waiting on a promise that never settles.\n callbackServer.off('error', onStartError);\n callbackServer.once('error', (err) => {\n clearTimeout(authTimeout);\n callbackServer.close();\n rejectToken(err);\n });\n\n const port = (callbackServer.address() as { port: number }).port;\n const redirectUri = `http://localhost:${port}/callback`;\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: oauthClientId,\n redirect_uri: redirectUri,\n scope: 'read write',\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n });\n\n const authUrl = `${authorizeBase}?${params.toString()}`;\n logger.debug('oauth_callback_listening', { port, redirectUri });\n logger.info('oauth_browser_opening');\n // Full authorize URL is debug-only: it carries the (public) client_id and\n // PKCE challenge — no secret — but stays out of default-level logs.\n logger.debug('oauth_authorize_url', { url: authUrl });\n // Always-on, ungated user-facing fallback: if the browser can't open, the\n // user must see this URL regardless of LOG_LEVEL — do NOT route it through\n // the (level-gated) logger.\n console.error(`Opening browser for Zendesk authentication...`);\n console.error(`If the browser doesn't open, visit: ${authUrl}`);\n\n open(authUrl)\n .then(() => {\n logger.debug('oauth_browser_opened');\n })\n .catch((err: unknown) => {\n // Do NOT swallow: this is the #60 signal. Capture why `open` failed\n // plus environment markers (presence only, never values).\n logger.error('oauth_browser_open_failed', {\n error: err instanceof Error ? err.message : String(err),\n errorCode: (err as NodeJS.ErrnoException | undefined)?.code,\n platform: process.platform,\n release: release(),\n isWsl: detectWsl(),\n hasSystemRoot: Boolean(process.env['SYSTEMROOT']),\n hasWindir: Boolean(process.env['WINDIR']),\n hasComSpec: Boolean(process.env['ComSpec']),\n hasPath: Boolean(process.env['PATH']),\n hasDisplay: Boolean(process.env['DISPLAY']),\n });\n });\n\n // Timeout after 5 minutes. Cleared on every completion path above so a\n // successful auth can't emit a spurious `oauth_timeout` error later.\n authTimeout = setTimeout(() => {\n logger.error('oauth_timeout', { timeoutMs: AUTH_TIMEOUT_MS });\n callbackServer.close();\n rejectToken(new Error('OAuth authentication timed out (5 min). Please try again.'));\n }, AUTH_TIMEOUT_MS);\n authTimeout.unref();\n\n resolveStarted({ authorizeUrl: authUrl, tokenPromise });\n });\n });\n};\n\n/**\n * Convenience wrapper that performs the full PKCE flow and resolves with the\n * token once the browser flow completes (blocking until then). Retained for\n * callers/tests that want the original \"await the token\" semantics.\n */\nexport const authenticateViaBrowser = async (\n config: BrowserOAuthConfig,\n logger: Logger = silentLogger,\n): Promise<TokenResult> => {\n const { tokenPromise } = await startBrowserAuth(config, logger);\n return tokenPromise;\n};\n\n/**\n * Exchange a refresh token for a fresh access token (and a rotated refresh token)\n * without any browser interaction. Public PKCE clients send no `client_secret`.\n * Zendesk refresh tokens are single-use: the caller MUST persist the new\n * `refresh_token` from the response. Throws on a non-2xx (expired/invalid\n * refresh token) so the caller can fall back to the full browser flow.\n */\nexport const refreshAccessToken = async (\n config: { subdomain: string; oauthClientId: string; refreshToken: string },\n logger: Logger = silentLogger,\n): Promise<TokenResult> => {\n const { tokenUrl } = getOAuthUrls(config.subdomain);\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: config.refreshToken,\n client_id: config.oauthClientId,\n });\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n logger.debug('oauth_token_refresh', { status: response.status });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Token refresh failed (${response.status}): ${errorBody}`);\n }\n\n const tokenData = (await response.json()) as TokenResult;\n logger.info('oauth_token_refreshed');\n return tokenData;\n};\n","import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport interface PackageInfo {\n name: string;\n version: string;\n}\n\n// Used only if package.json can't be located/parsed (should not happen in a\n// published install). Keeps the server bootable rather than throwing.\nconst FALLBACK: PackageInfo = {\n name: '@fruggr/zendesk-mcp-server',\n version: '0.0.0',\n};\n\n/**\n * Read `name`/`version` from the package's own package.json at runtime instead\n * of hardcoding them. Walks up from this module to the nearest package.json,\n * which resolves correctly both when bundled (`dist/index.js` → repo root) and\n * from source/tests (`src/` has no package.json, so the root is found). Reading\n * at runtime (not inlining at build) matters because semantic-release bumps the\n * version into package.json before publishing, after the build step.\n */\n// package.json can't change during the process lifetime, and createMcpServer\n// (hence this) may run several times (notably across tests) — cache the result.\nlet cached: PackageInfo | undefined;\n\nexport const readPackageInfo = (): PackageInfo => {\n if (cached) return cached;\n // Only readFileSync/JSON.parse can throw here (caught per-iteration below);\n // fileURLToPath/dirname/join don't, so no outer guard is needed.\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let depth = 0; depth < 8; depth++) {\n try {\n // JSON.parse yields `unknown`; validate at runtime rather than asserting.\n const raw: unknown = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));\n if (raw && typeof raw === 'object') {\n const pkg = raw as Partial<PackageInfo>;\n if (typeof pkg.name === 'string' && typeof pkg.version === 'string') {\n cached = { name: pkg.name, version: pkg.version };\n return cached;\n }\n }\n } catch {\n // No readable/valid package.json here — keep walking up.\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n cached = FALLBACK;\n return cached;\n};\n","import { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { type Logger, silentLogger } from '../utils/logger';\nimport { readPackageInfo } from '../utils/package-info';\n\n/**\n * On-disk OAuth token record. `expiresAt` is an absolute epoch-ms timestamp\n * (derived from the OAuth `expires_in`) so expiry survives restarts; `undefined`\n * means a non-expiring token (Zendesk's default until expiration is enabled).\n */\nexport interface PersistedToken {\n accessToken: string;\n refreshToken?: string | undefined;\n expiresAt?: number | undefined;\n}\n\nconst isWindows = process.platform === 'win32';\n\n/**\n * Config-dir segments derived from the *scoped* package name\n * (`@fruggr/zendesk-mcp-server` → `fruggr` + `zendesk-mcp-server`) so the path is\n * vendor-namespaced and can't collide with another `zendesk-mcp-server`.\n */\nconst appDirSegments = (): string[] => {\n const { name } = readPackageInfo();\n const scoped = /^@([^/]+)\\/(.+)$/.exec(name);\n return scoped?.[1] && scoped[2] ? [scoped[1], scoped[2]] : [name];\n};\n\n// OS config dir holding the per-subdomain token files: `%APPDATA%` on Windows,\n// `$XDG_CONFIG_HOME` (falling back to `~/.config`) elsewhere.\nconst configDir = (): string => {\n const segments = appDirSegments();\n if (isWindows) {\n const base = process.env['APPDATA'] ?? join(homedir(), 'AppData', 'Roaming');\n return join(base, ...segments);\n }\n const base = process.env['XDG_CONFIG_HOME'] ?? join(homedir(), '.config');\n return join(base, ...segments);\n};\n\n// Zendesk subdomains are [a-z0-9-]; sanitize defensively so a crafted value\n// can neither escape the config dir nor smuggle a path separator.\nconst safeName = (subdomain: string): string => subdomain.replace(/[^a-z0-9-]/gi, '_');\n\n/**\n * Path to the token file for a subdomain. Each subdomain gets its **own** file\n * (`<subdomain>.json`, a single record) so concurrent processes for different\n * subdomains never read-modify-write a shared file — no merge, no clobber.\n * `ZENDESK_TOKEN_FILE` overrides with an explicit path (a single file; use the\n * default layout for multi-subdomain installs).\n */\nexport const resolveTokenPath = (subdomain: string): string => {\n const override = process.env['ZENDESK_TOKEN_FILE'];\n if (override) return override;\n return join(configDir(), `${safeName(subdomain)}.json`);\n};\n\n// Atomic write (tmp + rename) so a crash mid-write can't leave a truncated file,\n// with owner-only perms on the file and its dir where the OS supports it.\nconst writeFileAtomic = (path: string, record: PersistedToken): void => {\n const dir = dirname(path);\n mkdirSync(dir, { recursive: true });\n const tmp = `${path}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(record, null, 2), 'utf8');\n if (!isWindows) chmodSync(tmp, 0o600);\n renameSync(tmp, path);\n if (!isWindows) {\n try {\n chmodSync(dir, 0o700);\n } catch {\n // Best-effort: tightening the dir is a bonus, not a requirement.\n }\n }\n};\n\n// A missing file or unparseable/garbage content is treated as \"no token yet\"\n// rather than an error: persistence must never wedge the auth flow.\nexport const loadToken = (path: string): PersistedToken | undefined => {\n try {\n const raw: unknown = JSON.parse(readFileSync(path, 'utf8'));\n if (raw && typeof raw === 'object' && typeof (raw as PersistedToken).accessToken === 'string') {\n return raw as PersistedToken;\n }\n } catch {\n // absent or corrupt → no token\n }\n return undefined;\n};\n\nexport const saveToken = (\n path: string,\n record: PersistedToken,\n logger: Logger = silentLogger,\n): void => {\n try {\n writeFileAtomic(path, record);\n logger.debug('token_persisted');\n } catch (err) {\n // Never fail the auth flow because the token couldn't be cached to disk.\n logger.warn('token_persist_failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n};\n\nexport const clearToken = (path: string, logger: Logger = silentLogger): void => {\n try {\n rmSync(path, { force: true });\n logger.debug('token_cleared');\n } catch (err) {\n logger.warn('token_clear_failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n};\n","import { type Logger, silentLogger } from '../utils/logger';\nimport { refreshAccessToken, startBrowserAuth } from './browser-oauth';\nimport {\n clearToken as clearPersistedToken,\n loadToken,\n type PersistedToken,\n resolveTokenPath,\n saveToken,\n} from './token-persistence';\n\ntype StoredToken = PersistedToken;\n\n// Refresh slightly before the real expiry so a request never races a token that\n// dies in flight.\nconst EXPIRY_SKEW_MS = 60_000;\n\n/**\n * Signals that interactive sign-in is required. The message is user-facing: MCP\n * surfaces it as the tool-call error text, so it carries the authorize URL and\n * tells the user to authenticate and retry. Kept as an Error factory (not a\n * class) to match the repo's functional style.\n */\nexport type AuthRequiredError = Error & { readonly authorizeUrl: string };\n\nexport const createAuthRequiredError = (authorizeUrl: string): AuthRequiredError =>\n Object.assign(\n new Error(\n 'Zendesk authentication required. A browser window should have opened for you to sign in. ' +\n 'If it did not, open this URL in your browser, then retry your request:\\n' +\n authorizeUrl,\n ),\n { name: 'AuthRequiredError', authorizeUrl } as const,\n );\n\nexport const isAuthRequiredError = (err: unknown): err is AuthRequiredError =>\n err instanceof Error &&\n err.name === 'AuthRequiredError' &&\n typeof (err as AuthRequiredError).authorizeUrl === 'string';\n\n// Compute the absolute expiry (epoch ms) from an OAuth `expires_in` (seconds),\n// or `undefined` for a non-expiring token.\nconst expiryFrom = (expiresIn: number | undefined): number | undefined =>\n typeof expiresIn === 'number' ? Date.now() + expiresIn * 1000 : undefined;\n\nexport const createTokenStore = (\n config: { subdomain: string; oauthClientId: string; callbackPort?: number | undefined },\n logger: Logger = silentLogger,\n) => {\n const tokenPath = resolveTokenPath(config.subdomain);\n // Seed the in-memory cache from disk so a restart (notably the Cowork-on-Windows\n // process churn) reuses the existing token instead of re-prompting.\n let token: StoredToken | undefined = loadToken(tokenPath);\n if (token) logger.debug('oauth_token_loaded_from_disk');\n\n // The authorize URL of the in-flight flow (set once the callback server is\n // listening); `undefined` means no flow is currently pending.\n let authorizeUrl: string | undefined;\n // Resolves to the authorize URL while a flow is being started; guards against\n // launching multiple browser flows for concurrent first calls.\n let starting: Promise<string> | undefined;\n // De-dupes concurrent refresh attempts so a burst of expired-token calls\n // triggers a single network round-trip.\n let refreshing: Promise<string | undefined> | undefined;\n\n const persist = (t: StoredToken): void => saveToken(tokenPath, t, logger);\n\n const setToken = (accessToken: string, refreshToken?: string | undefined) => {\n token = { accessToken, refreshToken };\n persist(token);\n };\n\n const isExpired = (t: StoredToken): boolean =>\n typeof t.expiresAt === 'number' && Date.now() >= t.expiresAt - EXPIRY_SKEW_MS;\n\n // Try to silently mint a fresh access token from the stored refresh token.\n // Resolves to the new access token, or `undefined` if there's nothing to\n // refresh / the refresh failed (in which case the dead token is dropped).\n const tryRefresh = async (current: StoredToken): Promise<string | undefined> => {\n if (!current.refreshToken) return undefined;\n try {\n const result = await refreshAccessToken(\n {\n subdomain: config.subdomain,\n oauthClientId: config.oauthClientId,\n refreshToken: current.refreshToken,\n },\n logger,\n );\n // Zendesk rotates the refresh token on every use — persist the new one (or\n // keep the current one if the response omitted it).\n token = {\n accessToken: result.access_token,\n refreshToken: result.refresh_token ?? current.refreshToken,\n expiresAt: expiryFrom(result.expires_in),\n };\n persist(token);\n logger.info('oauth_token_refreshed_cached');\n return token.accessToken;\n } catch (err) {\n logger.warn('oauth_token_refresh_failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n // Refresh token expired/invalid → drop it so we fall back to a fresh flow.\n token = undefined;\n clearPersistedToken(tokenPath, logger);\n return undefined;\n }\n };\n\n const beginAuth = (): Promise<string> => {\n logger.info('oauth_auth_start');\n return startBrowserAuth(\n {\n subdomain: config.subdomain,\n oauthClientId: config.oauthClientId,\n callbackPort: config.callbackPort,\n },\n logger,\n )\n .then((started) => {\n authorizeUrl = started.authorizeUrl;\n started.tokenPromise\n .then((result) => {\n token = {\n accessToken: result.access_token,\n refreshToken: result.refresh_token,\n expiresAt: expiryFrom(result.expires_in),\n };\n persist(token);\n logger.info('oauth_token_cached');\n })\n .catch((err) => {\n logger.warn('oauth_auth_failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n })\n .finally(() => {\n // Let the next call start a fresh flow: on success the cached token\n // short-circuits anyway; on failure/timeout we want a clean retry.\n starting = undefined;\n authorizeUrl = undefined;\n });\n return started.authorizeUrl;\n })\n .catch((err) => {\n // The callback server couldn't even start listening (e.g. port in use).\n // Reset so a later call can retry, and surface the underlying error.\n starting = undefined;\n throw err;\n });\n };\n\n const getToken = async (): Promise<string> => {\n if (token && !isExpired(token)) {\n logger.debug('oauth_token_cache_hit');\n return token.accessToken;\n }\n\n // Expired (or near-expiry) but refreshable → refresh silently before falling\n // back to a browser prompt. Concurrent callers share the one attempt.\n if (token?.refreshToken) {\n if (!refreshing) {\n refreshing = tryRefresh(token).finally(() => {\n refreshing = undefined;\n });\n }\n const refreshed = await refreshing;\n if (refreshed) return refreshed;\n }\n\n if (!starting) {\n starting = beginAuth();\n }\n\n // Fail fast: don't hold the tool call open for the whole browser flow.\n // Surface the authorize URL so the user can sign in, then retry — the\n // callback server keeps running in the background and caches the token,\n // so the next call succeeds.\n const url = authorizeUrl ?? (await starting);\n throw createAuthRequiredError(url);\n };\n\n // Backstop for an access token the server rejected mid-life (e.g. revoked).\n // A 401 invalidates the *access* token, not necessarily the refresh token —\n // so keep the latter and just mark the access token expired, letting the next\n // getToken attempt a silent refresh before falling back to the browser. Only\n // when there's no refresh token do we wipe the record entirely.\n const invalidate = (): void => {\n if (token?.refreshToken) {\n token = { accessToken: token.accessToken, refreshToken: token.refreshToken, expiresAt: 0 };\n persist(token);\n } else {\n token = undefined;\n clearPersistedToken(tokenPath, logger);\n }\n logger.info('oauth_token_invalidated');\n };\n\n return { getToken, setToken, invalidate };\n};\n","import * as z from 'zod/v4';\n\nexport const ToolMode = z.enum(['single', 'namespace', 'all']);\nexport type ToolMode = z.infer<typeof ToolMode>;\n\nexport const LogLevel = z.enum(['debug', 'info', 'warn', 'error']);\nexport type LogLevel = z.infer<typeof LogLevel>;\n\nexport const Namespace = z.enum(['tickets', 'help_center', 'users']);\nexport type Namespace = z.infer<typeof Namespace>;\n\nexport const Transport = z.enum(['stdio', 'http']);\nexport type Transport = z.infer<typeof Transport>;\n\nexport const ConfigSchema = z.object({\n subdomain: z.string().min(1, 'ZENDESK_SUBDOMAIN is required'),\n oauthClientId: z.string().min(1),\n zendeskEmail: z.string().optional(),\n zendeskApiToken: z.string().optional(),\n logLevel: LogLevel,\n mode: ToolMode,\n readOnly: z.boolean(),\n namespaces: z.array(Namespace).optional(),\n tools: z.array(z.string()).optional(),\n transport: Transport,\n host: z.string().min(1),\n port: z.number().int().min(0).max(65535),\n publicUrl: z.string().url().optional(),\n /**\n * Additional browser origins allowed by CORS in HTTP mode. The default\n * allowlist (the major web MCP clients + localhost-any-port for dev) is\n * always applied; this list extends it. Native MCP clients (Claude\n * Desktop, Claude Code CLI, Cursor, VS Code, Zed…) are unaffected\n * because they send no Origin header.\n */\n corsOrigins: z\n .array(\n z\n .string()\n .url()\n // Browsers send `Origin` with no trailing slash or path, and the CORS\n // allowlist matches by strict equality. Normalize the configured value\n // to its origin so `https://app.example.com/` (natural when\n // copy-pasting a URL) still matches at runtime.\n .transform((value) => new URL(value).origin)\n .refine((origin) => origin !== 'null', {\n message: 'CORS origin must be an http(s) URL with a host',\n }),\n )\n .default([]),\n callbackPort: z.number().int().min(1).max(65535).optional(),\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\ninterface CliResult {\n subdomain?: string;\n mode?: string;\n readOnly?: boolean;\n namespaces?: string[];\n tools?: string[];\n logLevel?: string;\n transport?: string;\n host?: string;\n port?: number;\n publicUrl?: string;\n corsOrigins?: string[];\n callbackPort?: number;\n}\n\n// Number.parseInt('8080abc', 10) === 8080 — silently accepts a numeric\n// prefix. Validate strictly so malformed values fail loudly instead. Range\n// checks (0 vs 1 minimum etc.) stay in ConfigSchema, the single authority.\n//\n// The error intentionally does NOT echo the offending value: when `label`\n// names a variable CodeQL's heuristics treat as sensitive (anything with\n// \"OAUTH\" / \"TOKEN\" / etc. in the name, like ZENDESK_OAUTH_CALLBACK_PORT),\n// reflecting `raw` into a thrown Error.message that bubbles up to the\n// `console.error('Fatal error:', error)` in src/index.ts gets flagged as\n// `js/clear-text-logging`. The label alone tells the operator which knob is\n// wrong; they can re-read their env / CLI to see what they actually set.\nconst parsePort = (raw: string, label: string): number => {\n if (!/^\\d+$/.test(raw)) {\n throw new Error(`Invalid ${label} value. Expected an integer 0-65535.`);\n }\n return Number(raw);\n};\n\nconst parsePortEnv = (raw: string | undefined, label: string): number | undefined =>\n raw === undefined || raw === '' ? undefined : parsePort(raw, label);\n\nconst parseCliArgs = (args: string[]): CliResult => {\n const result: CliResult = {};\n let positionalIndex = 0;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === undefined) continue;\n const next = args[i + 1];\n\n if (arg === '--mode' && next) {\n result.mode = next;\n i++;\n } else if (arg === '--read-only') {\n result.readOnly = true;\n } else if (arg === '--namespace' && next) {\n result.namespaces = result.namespaces ?? [];\n result.namespaces.push(next);\n i++;\n } else if (arg === '--tool' && next) {\n result.tools = result.tools ?? [];\n result.tools.push(next);\n i++;\n } else if (arg === '--log-level' && next) {\n result.logLevel = next;\n i++;\n } else if (arg === '--transport' && next) {\n result.transport = next;\n i++;\n } else if (arg === '--host' && next) {\n result.host = next;\n i++;\n } else if (arg === '--port' && next) {\n result.port = parsePort(next, '--port');\n i++;\n } else if (arg === '--public-url' && next) {\n result.publicUrl = next;\n i++;\n } else if (arg === '--cors-origin' && next) {\n result.corsOrigins = result.corsOrigins ?? [];\n result.corsOrigins.push(next);\n i++;\n } else if (arg === '--callback-port' && next) {\n result.callbackPort = parsePort(next, '--callback-port');\n i++;\n } else if (!arg.startsWith('-') && positionalIndex === 0) {\n result.subdomain = arg;\n positionalIndex++;\n }\n }\n\n return result;\n};\n\nexport const loadConfig = (argv: string[] = process.argv.slice(2)): Config => {\n const cli = parseCliArgs(argv);\n\n const subdomain = cli.subdomain ?? process.env['ZENDESK_SUBDOMAIN'] ?? '';\n const oauthClientId =\n process.env['ZENDESK_OAUTH_CLIENT_ID'] ?? (subdomain ? `${subdomain}_zendesk` : '');\n\n const mode = cli.tools?.length ? 'all' : (cli.mode ?? 'namespace');\n\n const transport = cli.transport ?? process.env['TRANSPORT'] ?? 'stdio';\n const host = cli.host ?? process.env['HOST'] ?? '0.0.0.0';\n const port = cli.port ?? parsePortEnv(process.env['PORT'], 'PORT') ?? 3000;\n const publicUrl = cli.publicUrl ?? process.env['PUBLIC_URL'];\n\n // CORS allowlist extension: CLI flags first, then comma-separated env var.\n // The defaults (major web MCP clients + localhost-any-port) are baked into\n // the HTTP transport — this list ADDS to them, never replaces them.\n const corsFromEnv = (process.env['CORS_ORIGIN'] ?? '')\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n const corsOrigins = [...(cli.corsOrigins ?? []), ...corsFromEnv];\n\n const callbackPort =\n cli.callbackPort ??\n parsePortEnv(process.env['ZENDESK_OAUTH_CALLBACK_PORT'], 'ZENDESK_OAUTH_CALLBACK_PORT');\n\n const zendeskEmail = process.env['ZENDESK_EMAIL'];\n const zendeskApiToken = process.env['ZENDESK_API_TOKEN'];\n\n // API token auth in HTTP mode would expose the issuing user's full rights to\n // anyone reaching the server (shared static credential). Refuse only when\n // BOTH are set — a stray ZENDESK_EMAIL in the shell environment is harmless\n // by itself, and rejecting it would surprise operators who intended OAuth.\n if (transport === 'http' && zendeskEmail && zendeskApiToken) {\n throw new Error(\n 'API token authentication (ZENDESK_EMAIL + ZENDESK_API_TOKEN) is not supported in HTTP mode. ' +\n 'HTTP mode requires per-user OAuth 2.1 PKCE - unset these variables and configure your ' +\n 'MCP client to perform the OAuth flow against Zendesk.',\n );\n }\n\n return ConfigSchema.parse({\n subdomain,\n oauthClientId,\n zendeskEmail,\n zendeskApiToken,\n logLevel: cli.logLevel ?? process.env['LOG_LEVEL'] ?? 'info',\n mode,\n readOnly: cli.readOnly ?? false,\n namespaces: cli.namespaces,\n tools: cli.tools,\n transport,\n host,\n port,\n publicUrl,\n corsOrigins,\n callbackPort,\n });\n};\n","import { getBaseUrl, getHelpCenterBaseUrl } from '../constants.js';\n\nexport class ZendeskApiError extends Error {\n constructor(\n public readonly status: number,\n public readonly statusText: string,\n public readonly body: string,\n ) {\n super(ZendeskApiError.buildMessage(status, statusText, body));\n this.name = 'ZendeskApiError';\n }\n\n private static buildMessage(status: number, statusText: string, body: string): string {\n switch (status) {\n case 401:\n return 'Authentication failed. Your Zendesk token may be expired or invalid. Re-authenticate to get a new token.';\n case 403:\n return 'Permission denied. Your Zendesk account does not have access to this resource.';\n case 404:\n return `Resource not found. Please verify the ID is correct. (${statusText})`;\n case 422:\n return `Validation error: ${body}`;\n case 429:\n return 'Rate limit exceeded. Please wait before making more requests.';\n default:\n return `Zendesk API error ${status}: ${statusText}. ${body}`;\n }\n }\n}\n\nexport interface ZendeskRequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE';\n body?: unknown;\n params?: Record<string, string>;\n}\n\n// token is either a Bearer OAuth token or a \"Basic xxx\" string (stdio API token mode)\nconst buildAuthHeader = (token: string): string =>\n token.startsWith('Basic ') ? token : `Bearer ${token}`;\n\nconst buildUrl = (base: string, path: string, params?: Record<string, string>): string => {\n const url = new URL(`${base}${path}`);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value);\n }\n }\n return url.toString();\n};\n\nconst executeRequest = async <T>(\n url: string,\n token: string,\n options: ZendeskRequestOptions = {},\n): Promise<T> => {\n const { method = 'GET', body } = options;\n\n const headers: Record<string, string> = {\n Authorization: buildAuthHeader(token),\n Accept: 'application/json',\n };\n\n if (body) {\n headers['Content-Type'] = 'application/json';\n }\n\n const init: RequestInit = { method, headers };\n if (body) {\n init.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, init);\n\n if (!response.ok) {\n const responseBody = await response.text();\n throw new ZendeskApiError(response.status, response.statusText, responseBody);\n }\n\n if (response.status === 204) {\n return {} as T;\n }\n\n return response.json() as Promise<T>;\n};\n\nexport const zendeskGet = <T>(\n subdomain: string,\n token: string,\n path: string,\n params?: Record<string, string>,\n): Promise<T> => {\n const url = buildUrl(getBaseUrl(subdomain), path, params);\n return executeRequest<T>(url, token);\n};\n\nexport const zendeskPost = <T>(\n subdomain: string,\n token: string,\n path: string,\n body: unknown,\n): Promise<T> => {\n const url = buildUrl(getBaseUrl(subdomain), path);\n return executeRequest<T>(url, token, { method: 'POST', body });\n};\n\nexport const zendeskPut = <T>(\n subdomain: string,\n token: string,\n path: string,\n body: unknown,\n): Promise<T> => {\n const url = buildUrl(getBaseUrl(subdomain), path);\n return executeRequest<T>(url, token, { method: 'PUT', body });\n};\n\nexport const helpCenterGet = <T>(\n subdomain: string,\n token: string,\n path: string,\n params?: Record<string, string>,\n): Promise<T> => {\n const url = buildUrl(getHelpCenterBaseUrl(subdomain), path, params);\n return executeRequest<T>(url, token);\n};\n\nexport const helpCenterPost = <T>(\n subdomain: string,\n token: string,\n path: string,\n body: unknown,\n): Promise<T> => {\n const url = buildUrl(getHelpCenterBaseUrl(subdomain), path);\n return executeRequest<T>(url, token, { method: 'POST', body });\n};\n\nexport const helpCenterPut = <T>(\n subdomain: string,\n token: string,\n path: string,\n body: unknown,\n): Promise<T> => {\n const url = buildUrl(getHelpCenterBaseUrl(subdomain), path);\n return executeRequest<T>(url, token, { method: 'PUT', body });\n};\n\nexport const fetchZendeskBinary = async (\n subdomain: string,\n token: string,\n contentUrl: string,\n): Promise<{ data: Buffer; contentType: string }> => {\n const expectedHost = `${subdomain}.zendesk.com`;\n const headers: Record<string, string> = {};\n if (new URL(contentUrl).hostname === expectedHost) {\n headers['Authorization'] = buildAuthHeader(token);\n }\n const response = await fetch(contentUrl, { headers });\n if (!response.ok) {\n const body = await response.text();\n throw new ZendeskApiError(response.status, response.statusText, body);\n }\n const contentType = response.headers.get('content-type') ?? 'application/octet-stream';\n const arrayBuffer = await response.arrayBuffer();\n return { data: Buffer.from(arrayBuffer), contentType };\n};\n\nexport const helpCenterUpload = async <T>(\n subdomain: string,\n token: string,\n path: string,\n formData: FormData,\n): Promise<T> => {\n const url = buildUrl(getHelpCenterBaseUrl(subdomain), path);\n const response = await fetch(url, {\n method: 'POST',\n headers: { Authorization: buildAuthHeader(token) },\n body: formData,\n });\n\n if (!response.ok) {\n const responseBody = await response.text();\n throw new ZendeskApiError(response.status, response.statusText, responseBody);\n }\n\n return response.json() as Promise<T>;\n};\n","import type { Namespace } from '../config';\nimport type { ToolDefinition } from '../tools/definitions';\n\nexport interface FilterOptions {\n readOnly: boolean;\n namespaces?: Namespace[] | undefined;\n tools?: string[] | undefined;\n}\n\nexport const filterTools = (allTools: ToolDefinition[], options: FilterOptions): ToolDefinition[] =>\n allTools.filter((tool) => {\n if (options.readOnly && !tool.readOnly) return false;\n if (options.namespaces?.length && !options.namespaces.includes(tool.namespace)) return false;\n if (options.tools?.length && !options.tools.includes(tool.name)) return false;\n return true;\n });\n\nexport const groupByNamespace = (tools: ToolDefinition[]): Map<string, ToolDefinition[]> => {\n const grouped = new Map<string, ToolDefinition[]>();\n for (const tool of tools) {\n const existing = grouped.get(tool.namespace) ?? [];\n existing.push(tool);\n grouped.set(tool.namespace, existing);\n }\n return grouped;\n};\n","import * as cheerio from 'cheerio';\nimport type { Element } from 'hast';\nimport { toHtml } from 'hast-util-to-html';\nimport type { Handle } from 'hast-util-to-mdast';\nimport rehypeParse from 'rehype-parse';\nimport rehypeRaw from 'rehype-raw';\nimport rehypeRemark from 'rehype-remark';\nimport rehypeStringify from 'rehype-stringify';\nimport remarkGfm from 'remark-gfm';\nimport remarkParse from 'remark-parse';\nimport remarkRehype from 'remark-rehype';\nimport remarkStringify from 'remark-stringify';\nimport { unified } from 'unified';\n\nexport interface Section {\n index: number;\n heading: string;\n headingTag: string;\n level: number;\n html: string;\n wordCount: number;\n}\n\nconst HEADING_LEVELS = new Set(['h1', 'h2', 'h3']);\n\nconst countWords = (text: string): number => {\n const trimmed = text.trim();\n if (!trimmed) return 0;\n return trimmed.split(/\\s+/).length;\n};\n\nconst textOf = (html: string): string => {\n if (!html) return '';\n const $ = cheerio.load(`<div>${html}</div>`, null, false);\n return $('div').first().text();\n};\n\nexport const parseSections = (html: string): Section[] => {\n if (!html?.trim()) return [];\n\n const $ = cheerio.load(html, null, false);\n const children = $.root().contents().toArray();\n\n const introParts: string[] = [];\n const sections: Array<{\n heading: string;\n headingTag: string;\n level: number;\n contentParts: string[];\n }> = [];\n let current: (typeof sections)[number] | null = null;\n\n for (const node of children) {\n const tagName = node.type === 'tag' ? node.name.toLowerCase() : '';\n\n if (HEADING_LEVELS.has(tagName)) {\n const level = Number.parseInt(tagName.slice(1), 10);\n current = {\n heading: $(node).text().trim(),\n headingTag: tagName,\n level,\n contentParts: [],\n };\n sections.push(current);\n continue;\n }\n\n const outer = $.html(node);\n if (current) {\n current.contentParts.push(outer);\n } else {\n introParts.push(outer);\n }\n }\n\n const result: Section[] = [];\n\n if (introParts.length > 0) {\n const introHtml = introParts.join('');\n result.push({\n index: 0,\n heading: 'intro',\n headingTag: '',\n level: 0,\n html: introHtml,\n wordCount: countWords(textOf(introHtml)),\n });\n }\n\n for (const s of sections) {\n const sectionHtml = s.contentParts.join('');\n result.push({\n index: result.length,\n heading: s.heading,\n headingTag: s.headingTag,\n level: s.level,\n html: sectionHtml,\n wordCount: countWords(textOf(sectionHtml)),\n });\n }\n\n return result;\n};\n\nexport const replaceSectionContent = (\n html: string,\n sectionIndex: number,\n newHtml: string,\n): string => {\n const sections = parseSections(html);\n if (sectionIndex < 0 || sectionIndex >= sections.length) {\n throw new Error(\n `Section index ${sectionIndex} out of range (valid: 0-${Math.max(0, sections.length - 1)})`,\n );\n }\n\n return sections\n .map((section, idx) => {\n const content = idx === sectionIndex ? newHtml : section.html;\n if (section.level === 0) return content;\n return `<${section.headingTag}>${section.heading}</${section.headingTag}>${content}`;\n })\n .join('');\n};\n\n// Keep structural HTML that markdown flattens lossily: <pre> with inline <br>\n// collapses to a single line, and <table> cells with multiple <p> break GFM\n// pipe tables. Leaving them as raw HTML is safer for round-trip.\nconst keepAsHtml: Handle = (_state, node) => ({\n type: 'html',\n value: toHtml(node as Element),\n});\n\nconst htmlToMdProcessor = unified()\n .use(rehypeParse, { fragment: true })\n .use(rehypeRemark, { handlers: { table: keepAsHtml, pre: keepAsHtml } })\n .use(remarkGfm)\n .use(remarkStringify, { bullet: '-', emphasis: '_', fences: true });\n\nconst mdToHtmlProcessor = unified()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkRehype, { allowDangerousHtml: true })\n .use(rehypeRaw)\n .use(rehypeStringify);\n\nexport const htmlToMarkdown = (html: string): string => {\n if (!html) return '';\n return String(htmlToMdProcessor.processSync(html));\n};\n\nexport const markdownToHtml = (markdown: string): string => {\n if (!markdown) return '';\n return String(mdToHtmlProcessor.processSync(markdown));\n};\n","import { CHARACTER_LIMIT } from '../constants.js';\nimport type {\n PaginationMeta,\n ZendeskArticle,\n ZendeskArticleAttachment,\n ZendeskCategory,\n ZendeskComment,\n ZendeskContentTag,\n ZendeskLabel,\n ZendeskOrganization,\n ZendeskPermissionGroup,\n ZendeskSection,\n ZendeskTicket,\n ZendeskTranslation,\n ZendeskUser,\n ZendeskUserSegment,\n} from '../types.js';\n\nexport const truncateIfNeeded = (text: string): string => {\n if (text.length <= CHARACTER_LIMIT) return text;\n return `${text.slice(0, CHARACTER_LIMIT)}\\n\\n--- Response truncated (${text.length} chars, limit ${CHARACTER_LIMIT}). Use pagination or filters to reduce results. ---`;\n};\n\nconst formatPagination = (meta: PaginationMeta): string => {\n const parts = [`Results: ${meta.count}`];\n if (meta.has_more) {\n parts.push(`More available (cursor: ${meta.after_cursor})`);\n }\n return parts.join(' | ');\n};\n\nexport const formatTicket = (ticket: ZendeskTicket): string =>\n [\n `## Ticket #${ticket.id}: ${ticket.subject}`,\n `- **Status**: ${ticket.status} | **Priority**: ${ticket.priority ?? 'none'} | **Type**: ${ticket.type ?? 'none'}`,\n `- **Requester**: ${ticket.requester_id} | **Assignee**: ${ticket.assignee_id ?? 'unassigned'}`,\n `- **Tags**: ${ticket.tags.length > 0 ? ticket.tags.join(', ') : 'none'}`,\n `- **Created**: ${ticket.created_at} | **Updated**: ${ticket.updated_at}`,\n ticket.description ? `\\n${ticket.description}` : '',\n ]\n .filter(Boolean)\n .join('\\n');\n\nexport const formatComment = (comment: ZendeskComment): string => {\n const lines = [\n `### ${comment.public ? 'Public comment' : 'Internal note'} by ${comment.author_id}`,\n `*${comment.created_at}*`,\n ];\n if (comment.attachments?.length) {\n const summary = comment.attachments.map((a) => `#${a.id} (${a.content_type})`).join(', ');\n lines.push(`Attachments: ${summary}`);\n }\n lines.push('', comment.body);\n return lines.join('\\n');\n};\n\nexport const formatUser = (user: ZendeskUser): string =>\n [\n `## ${user.name} (${user.id})`,\n `- **Email**: ${user.email}`,\n `- **Role**: ${user.role}`,\n user.role_type != null ? `- **Role type**: ${user.role_type}` : '',\n `- **Active**: ${user.active}`,\n user.organization_id ? `- **Organization**: ${user.organization_id}` : '',\n ]\n .filter(Boolean)\n .join('\\n');\n\nexport const formatOrganization = (org: ZendeskOrganization): string =>\n [\n `## ${org.name} (${org.id})`,\n org.details ? `- **Details**: ${org.details}` : '',\n org.domain_names.length > 0 ? `- **Domains**: ${org.domain_names.join(', ')}` : '',\n org.tags.length > 0 ? `- **Tags**: ${org.tags.join(', ')}` : '',\n ]\n .filter(Boolean)\n .join('\\n');\n\nexport const formatArticleSummary = (article: ZendeskArticle): string =>\n [\n `## ${article.title} (${article.id})`,\n `- **Locale**: ${article.locale} | **Source locale**: ${article.source_locale}`,\n `- **Section**: ${article.section_id} | **Draft**: ${article.draft}`,\n typeof article.position === 'number' ? `- **Position**: ${article.position}` : '',\n article.label_names.length > 0 ? `- **Labels**: ${article.label_names.join(', ')}` : '',\n `- **Created**: ${article.created_at} | **Updated**: ${article.updated_at}`,\n ]\n .filter(Boolean)\n .join('\\n');\n\nexport const formatArticle = (article: ZendeskArticle): string =>\n [formatArticleSummary(article), '', article.body].join('\\n');\n\nexport const formatTranslationSummary = (translation: ZendeskTranslation): string =>\n [\n `## Translation: ${translation.locale} (${translation.id})`,\n `- **Title**: ${translation.title}`,\n `- **Draft**: ${translation.draft}`,\n `- **Updated**: ${translation.updated_at}`,\n ].join('\\n');\n\nexport const formatTranslation = (translation: ZendeskTranslation): string =>\n [formatTranslationSummary(translation), '', translation.body].join('\\n');\n\nexport const formatCategory = (category: ZendeskCategory): string =>\n `- **${category.name}** (${category.id}) — ${category.description || 'No description'}`;\n\nexport const formatSection = (section: ZendeskSection): string =>\n `- **${section.name}** (${section.id}) — Category: ${section.category_id} — ${section.description || 'No description'}`;\n\nexport const formatPermissionGroup = (group: ZendeskPermissionGroup): string =>\n `- **${group.name}** (${group.id})${group.built_in ? ' — Built-in' : ''}`;\n\nexport const formatContentTag = (tag: ZendeskContentTag): string => `- **${tag.name}** (${tag.id})`;\n\nexport const formatLabel = (label: ZendeskLabel): string => `- **${label.name}** (${label.id})`;\n\nexport const formatUserSegment = (segment: ZendeskUserSegment): string =>\n `- **${segment.name}** (${segment.id}) — ${segment.user_type}${segment.built_in ? ' — Built-in' : ''}`;\n\nexport const formatAttachment = (attachment: ZendeskArticleAttachment): string =>\n `- **${attachment.file_name}** (${attachment.id}) — ${attachment.content_type} — ${attachment.size} bytes`;\n\nexport const formatList = <T>(\n items: T[],\n formatter: (item: T) => string,\n meta?: PaginationMeta,\n): string => {\n const header = meta ? formatPagination(meta) : '';\n const body = items.map(formatter).join('\\n\\n');\n const text = [header, body].filter(Boolean).join('\\n\\n');\n return truncateIfNeeded(text);\n};\n","import type { PaginationMeta, ZendeskListResponse } from '../types';\n\n// Cursor-based pagination (for list endpoints: /tickets, /organizations, etc.)\nexport const buildCursorParams = (pageSize: number, cursor?: string): Record<string, string> => {\n const params: Record<string, string> = {\n 'page[size]': String(pageSize),\n };\n if (cursor) {\n params['page[after]'] = cursor;\n }\n return params;\n};\n\n// Offset-based pagination (for search endpoints: /search, /help_center/articles/search)\nexport const buildOffsetParams = (perPage: number, page?: number): Record<string, string> => {\n const params: Record<string, string> = {\n per_page: String(perPage),\n };\n if (page && page > 1) {\n params['page'] = String(page);\n }\n return params;\n};\n\nexport const extractPaginationMeta = <T>(response: ZendeskListResponse<T>): PaginationMeta => ({\n has_more: response.meta?.has_more ?? response.next_page != null,\n after_cursor: response.meta?.after_cursor ?? null,\n count: response.count ?? 0,\n});\n\n// For search responses — offset-based, count is always present\nexport const extractSearchPaginationMeta = <T>(\n response: ZendeskListResponse<T>,\n perPage: number,\n page: number,\n): PaginationMeta => {\n const count = response.count ?? 0;\n const has_more = count > page * perPage;\n return {\n has_more,\n after_cursor: has_more ? String(page + 1) : null,\n count,\n };\n};\n","import * as z from 'zod/v4';\nimport {\n helpCenterGet,\n helpCenterPost,\n helpCenterPut,\n helpCenterUpload,\n zendeskGet,\n zendeskPost,\n} from '../client/zendesk-api';\nimport {\n DEFAULT_PAGE_SIZE,\n LARGE_ARTICLE_BODY_CHARS,\n LARGE_ARTICLE_SECTION_COUNT,\n MAX_PAGE_SIZE,\n} from '../constants';\nimport type {\n ZendeskArticle,\n ZendeskArticleAttachment,\n ZendeskCategory,\n ZendeskContentTag,\n ZendeskLabel,\n ZendeskListResponse,\n ZendeskPermissionGroup,\n ZendeskSection,\n ZendeskTranslation,\n ZendeskUserSegment,\n} from '../types';\nimport {\n htmlToMarkdown,\n markdownToHtml,\n parseSections,\n replaceSectionContent,\n} from '../utils/article-sections';\nimport {\n formatArticle,\n formatArticleSummary,\n formatAttachment,\n formatCategory,\n formatContentTag,\n formatLabel,\n formatList,\n formatPermissionGroup,\n formatSection,\n formatTranslation,\n formatTranslationSummary,\n formatUserSegment,\n truncateIfNeeded,\n} from '../utils/formatting';\nimport {\n buildCursorParams,\n buildOffsetParams,\n extractPaginationMeta,\n extractSearchPaginationMeta,\n} from '../utils/pagination';\nimport type { ToolContext, ToolDefinition } from './definitions';\n\nconst largeArticleHint = (body: string, sectionCount: number): string | null => {\n if (body.length < LARGE_ARTICLE_BODY_CHARS && sectionCount < LARGE_ARTICLE_SECTION_COUNT) {\n return null;\n }\n return [\n `> ⚠ Large article (${body.length} chars, ${sectionCount} sections).`,\n '> For targeted edits, prefer get_article_outline + get_article_section +',\n '> update_article_section to avoid re-sending the full body on each write.',\n '',\n ].join('\\n');\n};\n\nexport const createHelpCenterTools = (ctx: ToolContext): ToolDefinition[] => {\n const { subdomain, getToken } = ctx;\n\n return [\n {\n name: 'search_articles',\n namespace: 'help_center',\n readOnly: true,\n title: 'Search Help Center Articles',\n description:\n 'Full-text search across Help Center articles (metadata only, no body). Use get_article for full content. Supports locale filtering. Returns total count.',\n inputSchema: z.object({\n query: z.string().min(1).describe('Search query'),\n locale: z.string().optional().describe('Filter by locale (e.g., \"en-us\", \"fr\")'),\n per_page: z\n .number()\n .int()\n .min(1)\n .max(MAX_PAGE_SIZE)\n .default(DEFAULT_PAGE_SIZE)\n .describe('Results per page'),\n page: z.number().int().min(1).default(1).describe('Page number'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { query, locale, per_page, page } = params as {\n query: string;\n locale?: string;\n per_page: number;\n page: number;\n };\n const token = await getToken();\n const p: Record<string, string> = { query, ...buildOffsetParams(per_page, page) };\n if (locale) p['locale'] = locale;\n const response = await helpCenterGet<ZendeskListResponse<ZendeskArticle>>(\n subdomain,\n token,\n '/articles/search',\n p,\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.results ?? [],\n formatArticleSummary,\n extractSearchPaginationMeta(response, per_page, page),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'get_article',\n namespace: 'help_center',\n readOnly: true,\n title: 'Get Help Center Article',\n description:\n 'Retrieve an article by ID with full body content. For large articles, prefer get_article_outline + get_article_section to save tokens. Optionally specify locale for a translated version. Returns body (HTML), metadata, source_locale, and list of available translations.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n locale: z.string().optional().describe('Locale for translated version'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale } = params as { article_id: number; locale?: string };\n const token = await getToken();\n const path = locale ? `/${locale}/articles/${article_id}` : `/articles/${article_id}`;\n const { article } = await helpCenterGet<{ article: ZendeskArticle }>(\n subdomain,\n token,\n path,\n );\n const { translations } = await helpCenterGet<{ translations: ZendeskTranslation[] }>(\n subdomain,\n token,\n `/articles/${article_id}/translations`,\n );\n const hint = largeArticleHint(article.body, parseSections(article.body).length);\n const text =\n (hint ?? '') +\n formatArticle(article) +\n `\\n\\n**Available translations**: ${translations.map((t) => t.locale).join(', ')}`;\n return { content: [{ type: 'text', text: truncateIfNeeded(text) }] };\n },\n },\n {\n name: 'list_categories',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Help Center Categories',\n description: 'List all Help Center categories. Optionally filter by locale.',\n inputSchema: z.object({\n locale: z.string().optional(),\n page_size: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),\n cursor: z.string().optional(),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { locale, page_size, cursor } = params as {\n locale?: string;\n page_size: number;\n cursor?: string;\n };\n const token = await getToken();\n const path = locale ? `/${locale}/categories` : '/categories';\n const response = await helpCenterGet<ZendeskListResponse<ZendeskCategory>>(\n subdomain,\n token,\n path,\n buildCursorParams(page_size, cursor),\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.categories ?? [],\n formatCategory,\n extractPaginationMeta(response),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'list_sections',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Help Center Sections',\n description: 'List sections, optionally filtered by category ID and locale.',\n inputSchema: z.object({\n category_id: z.number().int().optional(),\n locale: z.string().optional(),\n page_size: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),\n cursor: z.string().optional(),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { category_id, locale, page_size, cursor } = params as {\n category_id?: number;\n locale?: string;\n page_size: number;\n cursor?: string;\n };\n const token = await getToken();\n const path =\n category_id && locale\n ? `/${locale}/categories/${category_id}/sections`\n : category_id\n ? `/categories/${category_id}/sections`\n : locale\n ? `/${locale}/sections`\n : '/sections';\n const response = await helpCenterGet<ZendeskListResponse<ZendeskSection>>(\n subdomain,\n token,\n path,\n buildCursorParams(page_size, cursor),\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.sections ?? [],\n formatSection,\n extractPaginationMeta(response),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'list_articles',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Help Center Articles',\n description:\n 'List articles (metadata only, no body). Use get_article for full content. Optionally filter by section ID and locale. Supports sort_by (\"title\", \"created_at\", \"updated_at\") and include_translations: true to show available translation locales per article. Note: include_translations must be re-sent on each paginated request.',\n inputSchema: z.object({\n section_id: z.number().int().optional(),\n locale: z.string().optional(),\n page_size: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),\n cursor: z.string().optional(),\n sort_by: z\n .enum(['created_at', 'updated_at', 'position', 'title'])\n .default('position')\n .describe('Sort field'),\n sort_order: z.enum(['asc', 'desc']).default('asc').describe('Sort direction'),\n include_translations: z\n .boolean()\n .default(false)\n .describe(\n 'Include available translation locales per article (causes 1 extra API call per article)',\n ),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { section_id, locale, page_size, cursor, sort_by, sort_order, include_translations } =\n params as {\n section_id?: number;\n locale?: string;\n page_size: number;\n cursor?: string;\n sort_by: string;\n sort_order: string;\n include_translations: boolean;\n };\n const token = await getToken();\n const path =\n section_id && locale\n ? `/${locale}/sections/${section_id}/articles`\n : section_id\n ? `/sections/${section_id}/articles`\n : locale\n ? `/${locale}/articles`\n : '/articles';\n const response = await helpCenterGet<ZendeskListResponse<ZendeskArticle>>(\n subdomain,\n token,\n path,\n { ...buildCursorParams(page_size, cursor), sort_by, sort_order },\n );\n const articles = response.articles ?? [];\n if (!include_translations) {\n return {\n content: [\n {\n type: 'text',\n text: formatList(articles, formatArticleSummary, extractPaginationMeta(response)),\n },\n ],\n };\n }\n const formatted = await Promise.all(\n articles.map(async (article) => {\n const { translations } = await helpCenterGet<{ translations: ZendeskTranslation[] }>(\n subdomain,\n token,\n `/articles/${article.id}/translations`,\n );\n const locales = translations.map((t) => t.locale).join(', ');\n return `${formatArticleSummary(article)}\\n- **Translations**: ${locales}`;\n }),\n );\n const meta = extractPaginationMeta(response);\n const header = meta.count\n ? `Results: ${meta.count}${meta.has_more ? ` | More available (cursor: ${meta.after_cursor})` : ''}`\n : '';\n const text = [header, ...formatted].filter(Boolean).join('\\n\\n');\n return { content: [{ type: 'text', text: truncateIfNeeded(text) }] };\n },\n },\n {\n name: 'list_article_translations',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Article Translations',\n description:\n 'List all available translations for an article (metadata only, no body: locale, title, draft, updated_at). Use get_article with locale for full translated content.',\n inputSchema: z.object({ article_id: z.number().int().describe('Article ID') }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id } = params as { article_id: number };\n const token = await getToken();\n const { translations } = await helpCenterGet<{ translations: ZendeskTranslation[] }>(\n subdomain,\n token,\n `/articles/${article_id}/translations`,\n );\n return {\n content: [{ type: 'text', text: formatList(translations, formatTranslationSummary) }],\n };\n },\n },\n {\n name: 'create_article_translation',\n namespace: 'help_center',\n readOnly: false,\n title: 'Create Article Translation',\n description: 'Create a translation for an existing article in a specific locale.',\n inputSchema: z.object({\n article_id: z.number().int(),\n locale: z.string().describe('Target locale (e.g., \"fr\", \"de\")'),\n title: z.string().min(1),\n body: z.string().min(1).describe('Translated body (HTML)'),\n draft: z.boolean().default(false),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale, title, body, draft } = params as {\n article_id: number;\n locale: string;\n title: string;\n body: string;\n draft: boolean;\n };\n const token = await getToken();\n const { translation } = await helpCenterPost<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations`,\n { translation: { locale, title, body, draft } },\n );\n return {\n content: [\n {\n type: 'text',\n text: `Translation created for article #${article_id} in \"${locale}\".\\n\\n${formatTranslation(translation)}`,\n },\n ],\n };\n },\n },\n {\n name: 'update_article_translation',\n namespace: 'help_center',\n readOnly: false,\n title: 'Update Article Translation',\n description:\n \"Update article content (title, body) in a specific locale. For targeted edits on one or a few sections, prefer update_article_section — this tool replaces the FULL body and re-sends the entire article on each write. Use the article's source_locale (from get_article) for the default language, or another locale for translations.\",\n inputSchema: z.object({\n article_id: z.number().int(),\n locale: z.string(),\n title: z.string().optional(),\n body: z.string().optional(),\n draft: z.boolean().optional(),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale, ...updates } = params as {\n article_id: number;\n locale: string;\n } & Record<string, unknown>;\n const token = await getToken();\n const { translation } = await helpCenterPut<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${locale}`,\n { translation: updates },\n );\n return {\n content: [\n {\n type: 'text',\n text: `Translation updated for article #${article_id} in \"${locale}\".\\n\\n${formatTranslation(translation)}`,\n },\n ],\n };\n },\n },\n {\n name: 'list_permission_groups',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Permission Groups',\n description:\n 'List all Guide permission groups. Use this to find the permission_group_id required when creating articles.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async () => {\n const token = await getToken();\n const response = await zendeskGet<{\n permission_groups: ZendeskPermissionGroup[];\n count: number;\n }>(subdomain, token, '/guide/permission_groups');\n return {\n content: [\n {\n type: 'text',\n text: formatList(response.permission_groups ?? [], formatPermissionGroup),\n },\n ],\n };\n },\n },\n {\n name: 'create_article',\n namespace: 'help_center',\n readOnly: false,\n title: 'Create Help Center Article',\n description:\n \"Create a new article in a section. The locale becomes the article's source_locale. Requires a permission_group_id (use list_permission_groups to find available IDs). To add content in other locales afterwards, use create_article_translation.\",\n inputSchema: z.object({\n section_id: z.number().int(),\n title: z.string().min(1),\n body: z.string().min(1).describe('Article body (HTML)'),\n permission_group_id: z\n .number()\n .int()\n .describe('Permission group ID (use list_permission_groups to find it)'),\n user_segment_id: z\n .number()\n .int()\n .optional()\n .describe(\n 'User segment ID for visibility (use list_user_segments to find it). Defaults to everyone.',\n ),\n author_id: z\n .number()\n .int()\n .optional()\n .describe('Author user ID. Defaults to the authenticated user.'),\n content_tag_ids: z\n .array(z.string())\n .optional()\n .describe('Content tag IDs (use list_content_tags to find them)'),\n locale: z.string().optional(),\n draft: z.boolean().default(true),\n promoted: z.boolean().default(false),\n label_names: z\n .array(z.string())\n .optional()\n .describe('Label names for search ranking (use list_labels to see existing labels)'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { section_id, ...articleData } = params as { section_id: number } & Record<\n string,\n unknown\n >;\n const token = await getToken();\n const { article } = await helpCenterPost<{ article: ZendeskArticle }>(\n subdomain,\n token,\n `/sections/${section_id}/articles`,\n { article: articleData },\n );\n return {\n content: [\n { type: 'text', text: `Article #${article.id} created.\\n\\n${formatArticle(article)}` },\n ],\n };\n },\n },\n {\n name: 'update_article',\n namespace: 'help_center',\n readOnly: false,\n title: 'Update Help Center Article',\n description:\n 'Update article metadata only (draft, promoted, labels, tags, visibility, section, sort position, etc.). Does NOT update content (title, body) — use update_article_translation for that.',\n inputSchema: z.object({\n article_id: z.number().int(),\n draft: z.boolean().optional(),\n promoted: z.boolean().optional(),\n label_names: z.array(z.string()).optional().describe('Label names for search ranking'),\n content_tag_ids: z.array(z.string()).optional().describe('Content tag IDs'),\n user_segment_id: z.number().int().optional().describe('User segment ID for visibility'),\n author_id: z.number().int().optional().describe('Author user ID'),\n permission_group_id: z.number().int().optional().describe('Permission group ID'),\n section_id: z.number().int().optional(),\n position: z\n .number()\n .int()\n .min(0)\n .optional()\n .describe(\n 'Sort position within the section (manual ordering only; 0 = first/top). New articles default to position 0. To move an article to the END of its section, set this to one more than the highest current position: read the highest position P from list_articles with sort_by=\"position\", sort_order=\"desc\", then set position = P + 1.',\n ),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, ...updates } = params as { article_id: number } & Record<\n string,\n unknown\n >;\n const token = await getToken();\n const { article } = await helpCenterPut<{ article: ZendeskArticle }>(\n subdomain,\n token,\n `/articles/${article_id}`,\n { article: updates },\n );\n return {\n content: [\n { type: 'text', text: `Article #${article.id} updated.\\n\\n${formatArticle(article)}` },\n ],\n };\n },\n },\n {\n name: 'list_content_tags',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Content Tags',\n description:\n 'List all Guide content tags. Content tags are visible to end users and help them find related articles.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async () => {\n const token = await getToken();\n const response = await zendeskGet<{ records: ZendeskContentTag[]; count: number }>(\n subdomain,\n token,\n '/guide/content_tags',\n );\n return {\n content: [{ type: 'text', text: formatList(response.records ?? [], formatContentTag) }],\n };\n },\n },\n {\n name: 'create_content_tag',\n namespace: 'help_center',\n readOnly: false,\n title: 'Create Content Tag',\n description: 'Create a new content tag for Guide articles.',\n inputSchema: z.object({\n name: z.string().min(1).describe('Content tag name'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { name } = params as { name: string };\n const token = await getToken();\n const { record } = await zendeskPost<{ record: ZendeskContentTag }>(\n subdomain,\n token,\n '/guide/content_tags',\n { record: { name } },\n );\n return {\n content: [{ type: 'text', text: `Content tag created.\\n\\n${formatContentTag(record)}` }],\n };\n },\n },\n {\n name: 'list_labels',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Article Labels',\n description:\n 'List all article labels. Labels improve Help Center search ranking and are not visible to end users.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async () => {\n const token = await getToken();\n const response = await helpCenterGet<{ labels: ZendeskLabel[]; count: number }>(\n subdomain,\n token,\n '/articles/labels',\n );\n return {\n content: [{ type: 'text', text: formatList(response.labels ?? [], formatLabel) }],\n };\n },\n },\n {\n name: 'list_user_segments',\n namespace: 'help_center',\n readOnly: true,\n title: 'List User Segments',\n description:\n 'List all user segments. User segments control article visibility (who can view). Use the ID when creating or updating articles.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async () => {\n const token = await getToken();\n const response = await helpCenterGet<{\n user_segments: ZendeskUserSegment[];\n count: number;\n }>(subdomain, token, '/user_segments');\n return {\n content: [\n { type: 'text', text: formatList(response.user_segments ?? [], formatUserSegment) },\n ],\n };\n },\n },\n {\n name: 'list_article_attachments',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Article Attachments',\n description: 'List all attachments for an article.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id } = params as { article_id: number };\n const token = await getToken();\n const response = await helpCenterGet<{\n article_attachments: ZendeskArticleAttachment[];\n count: number;\n }>(subdomain, token, `/articles/${article_id}/attachments`);\n return {\n content: [\n {\n type: 'text',\n text: formatList(response.article_attachments ?? [], formatAttachment),\n },\n ],\n };\n },\n },\n {\n name: 'get_article_outline',\n namespace: 'help_center',\n readOnly: true,\n title: 'Get Article Outline',\n description:\n 'Return a compact outline of an article (list of sections delimited by h1/h2/h3, with word counts) for the given locale (defaults to source_locale). Includes available translations with their outdated status. Use get_article_section to fetch a specific section.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n locale: z\n .string()\n .optional()\n .describe('Locale of the body to outline (defaults to article source_locale)'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale } = params as { article_id: number; locale?: string };\n const token = await getToken();\n const { article } = await helpCenterGet<{ article: ZendeskArticle }>(\n subdomain,\n token,\n `/articles/${article_id}`,\n );\n const effectiveLocale = locale ?? article.source_locale;\n const { translation } = await helpCenterGet<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${effectiveLocale}`,\n );\n const { translations } = await helpCenterGet<{\n translations: Array<ZendeskTranslation & { outdated?: boolean }>;\n }>(subdomain, token, `/articles/${article_id}/translations`);\n const sections = parseSections(translation.body);\n\n const outlineLines = sections.length\n ? sections\n .map(\n (s) =>\n `- [${s.index}] ${s.headingTag ? `${s.headingTag}: ` : ''}${s.heading} (${s.wordCount} words)`,\n )\n .join('\\n')\n : '_(no sections detected)_';\n const translationsList = translations\n .map((t) => `- ${t.locale}${t.outdated ? ' (outdated)' : ''}`)\n .join('\\n');\n\n const text = [\n `# Outline — Article #${article_id} (${effectiveLocale})`,\n `**Title**: ${translation.title}`,\n '',\n '## Sections',\n outlineLines,\n '',\n '## Available translations',\n translationsList,\n ].join('\\n');\n return { content: [{ type: 'text', text }] };\n },\n },\n {\n name: 'get_article_section',\n namespace: 'help_center',\n readOnly: true,\n title: 'Get Article Section',\n description:\n 'Retrieve the content of a single section of an article in a given locale. Use get_article_outline first to discover section indexes. Default format=\"html\" for round-trip safety. Pass format=\"markdown\" only for human review — the Markdown representation is lossy on some structures (<pre> with <br>, tables with multi-<p> cells are kept as raw HTML to limit the damage, but do not round-trip markdown content back through update_article_section).',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n locale: z.string().describe('Locale of the body (e.g., \"en-us\", \"fr\")'),\n section_index: z\n .number()\n .int()\n .min(0)\n .describe('0-based index of the section (see get_article_outline)'),\n format: z\n .enum(['html', 'markdown'])\n .default('html')\n .describe(\n 'Output format. \"html\" (default) is round-trip safe. \"markdown\" is lossy on some HTML structures — use only for human review, not before update_article_section.',\n ),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale, section_index, format } = params as {\n article_id: number;\n locale: string;\n section_index: number;\n format: 'html' | 'markdown';\n };\n const token = await getToken();\n const { translation } = await helpCenterGet<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${locale}`,\n );\n const sections = parseSections(translation.body);\n const section = sections[section_index];\n if (!section) {\n throw new Error(\n `Section index ${section_index} not found. Article has ${sections.length} section(s) (0-${Math.max(0, sections.length - 1)}).`,\n );\n }\n const content = format === 'markdown' ? htmlToMarkdown(section.html) : section.html;\n const headerLine = section.headingTag\n ? `## [${section.index}] ${section.headingTag}: ${section.heading}`\n : `## [${section.index}] ${section.heading}`;\n const text = [\n headerLine,\n `_Locale: ${locale} | Words: ${section.wordCount} | Format: ${format}_`,\n '',\n content,\n ].join('\\n');\n return { content: [{ type: 'text', text: truncateIfNeeded(text) }] };\n },\n },\n {\n name: 'update_article_section',\n namespace: 'help_center',\n readOnly: false,\n title: 'Update Article Section',\n description:\n 'Replace the content of a single section of an article in a given locale, keeping the rest of the body intact. The server fetches the current body, replaces the targeted section, and PUTs the full reconstructed body via the Translations API. Default format=\"html\" for fidelity. Use format=\"markdown\" only when you control the input and know it does not rely on structures that round-trip poorly (code blocks with line breaks, tables with multi-paragraph cells). The section heading is preserved and is NOT part of the replaced content.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n locale: z.string().describe('Locale of the translation to update'),\n section_index: z\n .number()\n .int()\n .min(0)\n .describe('0-based index of the section to replace (see get_article_outline)'),\n content: z\n .string()\n .describe(\n 'New content for the section (heading excluded). HTML by default, Markdown if format=\"markdown\".',\n ),\n format: z\n .enum(['html', 'markdown'])\n .default('html')\n .describe(\n 'Input format. \"html\" (default) is the safe path. \"markdown\" is converted to HTML server-side but may introduce artifacts on complex content.',\n ),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale, section_index, content, format } = params as {\n article_id: number;\n locale: string;\n section_index: number;\n content: string;\n format: 'html' | 'markdown';\n };\n const token = await getToken();\n const { translation } = await helpCenterGet<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${locale}`,\n );\n const newSectionHtml = format === 'markdown' ? markdownToHtml(content) : content;\n const newBody = replaceSectionContent(translation.body, section_index, newSectionHtml);\n const { translation: updated } = await helpCenterPut<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${locale}`,\n { translation: { body: newBody } },\n );\n const updatedSections = parseSections(updated.body);\n const updatedSection = updatedSections[section_index];\n const newWordCount = updatedSection?.wordCount ?? 0;\n const headingLabel = updatedSection?.heading ?? '(intro)';\n const text = [\n `Section [${section_index}] \"${headingLabel}\" updated for article #${article_id} (${locale}).`,\n `New word count: ${newWordCount}.`,\n ].join('\\n');\n return { content: [{ type: 'text', text }] };\n },\n },\n {\n name: 'compare_translations',\n namespace: 'help_center',\n readOnly: true,\n title: 'Compare Article Translations',\n description:\n 'Compare section structure between two locales of the same article, matched by index. Returns a compact table (one row per section) with status: \"ok\" (both present, source/target word count ratio within 25%), \"different\" (word count ratio diverges by more than 25% — size signal only, NOT a semantic divergence: two locales may legitimately differ in verbosity) or \"missing\" (section absent in target). Useful to spot structurally stale or missing sections; do not interpret \"different\" as an edit regression on its own.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n source_locale: z.string().describe('Source (reference) locale'),\n target_locale: z.string().describe('Target locale to compare against source'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, source_locale, target_locale } = params as {\n article_id: number;\n source_locale: string;\n target_locale: string;\n };\n const token = await getToken();\n const [sourceRes, targetRes] = await Promise.all([\n helpCenterGet<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${source_locale}`,\n ),\n helpCenterGet<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${target_locale}`,\n ),\n ]);\n const sourceSections = parseSections(sourceRes.translation.body);\n const targetSections = parseSections(targetRes.translation.body);\n const maxLen = Math.max(sourceSections.length, targetSections.length);\n\n const rows: string[] = [];\n rows.push(`| Idx | Heading | Status | Source words | Target words |`);\n rows.push(`| --- | --- | --- | --- | --- |`);\n for (let i = 0; i < maxLen; i += 1) {\n const src = sourceSections[i];\n const tgt = targetSections[i];\n const heading = src?.heading ?? tgt?.heading ?? '';\n const sourceWords = src?.wordCount ?? 0;\n const targetWords = tgt?.wordCount ?? 0;\n let status: 'ok' | 'missing' | 'different';\n if (!tgt) status = 'missing';\n else if (!src) status = 'different';\n else {\n const denom = Math.max(sourceWords, 1);\n const diffRatio = Math.abs(sourceWords - targetWords) / denom;\n status = diffRatio > 0.25 ? 'different' : 'ok';\n }\n rows.push(`| ${i} | ${heading} | ${status} | ${sourceWords} | ${targetWords} |`);\n }\n\n const text = [\n `# Translation diff — Article #${article_id} (${source_locale} → ${target_locale})`,\n '',\n ...rows,\n ].join('\\n');\n return { content: [{ type: 'text', text }] };\n },\n },\n {\n name: 'create_article_attachment',\n namespace: 'help_center',\n readOnly: false,\n title: 'Create Article Attachment',\n description:\n 'Upload an attachment to an article. Provide file content as base64-encoded string.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n file_name: z.string().min(1).describe('File name (e.g., \"screenshot.png\")'),\n file_base64: z.string().min(1).describe('File content encoded as base64'),\n content_type: z\n .string()\n .default('application/octet-stream')\n .describe('MIME type (e.g., \"image/png\", \"application/pdf\")'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, file_name, file_base64, content_type } = params as {\n article_id: number;\n file_name: string;\n file_base64: string;\n content_type: string;\n };\n const token = await getToken();\n const buffer = Buffer.from(file_base64, 'base64');\n const blob = new Blob([buffer], { type: content_type });\n const formData = new FormData();\n formData.append('file', blob, file_name);\n const { article_attachment } = await helpCenterUpload<{\n article_attachment: ZendeskArticleAttachment;\n }>(subdomain, token, `/articles/${article_id}/attachments`, formData);\n return {\n content: [\n {\n type: 'text',\n text: `Attachment created for article #${article_id}.\\n\\n${formatAttachment(article_attachment)}`,\n },\n ],\n };\n },\n },\n ];\n};\n","import * as z from 'zod/v4';\nimport { zendeskGet } from '../client/zendesk-api';\nimport { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE } from '../constants';\nimport type { ZendeskListResponse } from '../types';\nimport { truncateIfNeeded } from '../utils/formatting';\nimport { buildOffsetParams, extractSearchPaginationMeta } from '../utils/pagination';\nimport type { ToolContext, ToolDefinition } from './definitions';\n\nconst formatSearchResult = (result: Record<string, unknown>): string => {\n const lines: string[] = [`## [${result['result_type']}] #${result['id']}`];\n if (result['subject']) lines.push(`**Subject**: ${result['subject']}`);\n if (result['name']) lines.push(`**Name**: ${result['name']}`);\n if (result['title']) lines.push(`**Title**: ${result['title']}`);\n if (result['email']) lines.push(`**Email**: ${result['email']}`);\n if (result['status']) lines.push(`**Status**: ${result['status']}`);\n if (result['description']) {\n const desc = String(result['description']);\n lines.push(desc.length > 200 ? `${desc.slice(0, 200)}...` : desc);\n }\n return lines.join('\\n');\n};\n\nexport const createSearchTools = (ctx: ToolContext): ToolDefinition[] => {\n const { subdomain, getToken } = ctx;\n\n return [\n {\n name: 'search',\n namespace: 'tickets',\n readOnly: true,\n title: 'Zendesk Unified Search',\n description:\n 'Search across tickets, users, and organizations. Supports filters like \"type:ticket status:open\", \"type:user role:agent\". Returns total count and paginated results (100 per page). Organization results include name and ID only — use get_organization for full details (tags, domains, details).',\n inputSchema: z.object({\n query: z.string().min(1).describe('Zendesk search query'),\n per_page: z\n .number()\n .int()\n .min(1)\n .max(MAX_PAGE_SIZE)\n .default(DEFAULT_PAGE_SIZE)\n .describe('Results per page (max 100)'),\n page: z.number().int().min(1).default(1).describe('Page number (1-based)'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { query, per_page, page } = params as {\n query: string;\n per_page: number;\n page: number;\n };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<Record<string, unknown>>>(\n subdomain,\n token,\n '/search',\n {\n query,\n ...buildOffsetParams(per_page, page),\n },\n );\n const results = response.results ?? [];\n const meta = extractSearchPaginationMeta(response, per_page, page);\n const header = `Total: ${meta.count} | Page ${page} (${results.length} results)${meta.has_more ? ` | Next page: ${meta.after_cursor}` : ''}`;\n const body = results.map(formatSearchResult).join('\\n\\n');\n return {\n content: [\n { type: 'text', text: truncateIfNeeded([header, body].filter(Boolean).join('\\n\\n')) },\n ],\n };\n },\n },\n ];\n};\n","import * as z from 'zod/v4';\nimport {\n fetchZendeskBinary,\n ZendeskApiError,\n zendeskGet,\n zendeskPost,\n zendeskPut,\n} from '../client/zendesk-api';\nimport {\n DEFAULT_PAGE_SIZE,\n MAX_ATTACHMENT_BYTES,\n MAX_COMMENT_PAGES,\n MAX_EMBEDDED_IMAGE_COUNT,\n MAX_PAGE_SIZE,\n} from '../constants';\nimport type {\n ZendeskComment,\n ZendeskListResponse,\n ZendeskTicket,\n ZendeskTicketAttachment,\n} from '../types';\nimport { formatComment, formatList, formatTicket, truncateIfNeeded } from '../utils/formatting';\nimport {\n buildCursorParams,\n buildOffsetParams,\n extractPaginationMeta,\n extractSearchPaginationMeta,\n} from '../utils/pagination';\nimport type { ToolContext, ToolDefinition, ToolImageContent, ToolTextContent } from './definitions';\n\nconst formatReference = (attachment: ZendeskTicketAttachment): string =>\n `**${attachment.file_name}** (id ${attachment.id}, ${attachment.content_type}, ${attachment.size} bytes) — ${attachment.content_url}`;\n\nconst buildEmbeddedImageBlocks = async (\n subdomain: string,\n token: string,\n attachment: ZendeskTicketAttachment,\n reference: string,\n): Promise<Array<ToolTextContent | ToolImageContent>> => {\n const { data, contentType } = await fetchZendeskBinary(subdomain, token, attachment.content_url);\n return [\n { type: 'image', data: data.toString('base64'), mimeType: contentType },\n { type: 'text', text: reference },\n ];\n};\n\n// Zendesk has no endpoint to list a ticket's attachments directly.\n// Attachments are always attached to comments, so the only way to collect\n// them all is to walk through every comment page and extract their attachments.\nconst fetchAllTicketComments = async (\n subdomain: string,\n token: string,\n ticketId: number,\n): Promise<ZendeskComment[]> => {\n const all: ZendeskComment[] = [];\n let cursor: string | undefined;\n let pages = 0;\n while (pages < MAX_COMMENT_PAGES) {\n const response = await zendeskGet<{\n comments: ZendeskComment[];\n meta?: { has_more: boolean; after_cursor: string };\n }>(subdomain, token, `/tickets/${ticketId}/comments`, buildCursorParams(MAX_PAGE_SIZE, cursor));\n all.push(...response.comments);\n pages += 1;\n if (!response.meta?.has_more || !response.meta?.after_cursor) break;\n cursor = response.meta.after_cursor;\n }\n return all;\n};\n\nconst collectAttachmentBlocks = async (\n subdomain: string,\n token: string,\n attachments: ZendeskTicketAttachment[],\n): Promise<Array<ToolTextContent | ToolImageContent>> => {\n const blocks: Array<ToolTextContent | ToolImageContent> = [];\n let embeddedCount = 0;\n\n for (const attachment of attachments) {\n const reference = formatReference(attachment);\n const isImage = attachment.content_type.startsWith('image/');\n\n if (!isImage) {\n blocks.push({ type: 'text', text: reference });\n continue;\n }\n\n let skipReason: string | null = null;\n if (attachment.size > MAX_ATTACHMENT_BYTES) {\n skipReason = 'skipped: exceeds 5 MB per-image limit';\n } else if (embeddedCount >= MAX_EMBEDDED_IMAGE_COUNT) {\n skipReason = `skipped: max ${MAX_EMBEDDED_IMAGE_COUNT} embedded images reached`;\n }\n\n if (skipReason) {\n blocks.push({ type: 'text', text: `${reference} — ${skipReason}` });\n continue;\n }\n\n try {\n blocks.push(...(await buildEmbeddedImageBlocks(subdomain, token, attachment, reference)));\n embeddedCount += 1;\n } catch (error) {\n const reason =\n error instanceof ZendeskApiError\n ? `download failed: ${error.status} ${error.statusText}`\n : 'download failed';\n blocks.push({ type: 'text', text: `${reference} — ${reason}` });\n }\n }\n\n return blocks;\n};\n\nexport const createTicketTools = (ctx: ToolContext): ToolDefinition[] => {\n const { subdomain, getToken } = ctx;\n\n return [\n {\n name: 'get_ticket',\n namespace: 'tickets',\n readOnly: true,\n title: 'Get Zendesk Ticket',\n description:\n 'Retrieve a Zendesk ticket by ID, including its comments if requested. Returns ticket details (subject, status, priority, assignee, tags, description) and optionally all comments/internal notes.',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n include_comments: z.boolean().default(false).describe('Include ticket comments'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, include_comments } = params as {\n ticket_id: number;\n include_comments: boolean;\n };\n const token = await getToken();\n const { ticket } = await zendeskGet<{ ticket: ZendeskTicket }>(\n subdomain,\n token,\n `/tickets/${ticket_id}`,\n );\n let text = formatTicket(ticket);\n if (include_comments) {\n const { comments } = await zendeskGet<{ comments: ZendeskComment[] }>(\n subdomain,\n token,\n `/tickets/${ticket_id}/comments`,\n );\n text += `\\n\\n---\\n# Comments\\n\\n${comments.map(formatComment).join('\\n\\n')}`;\n }\n return { content: [{ type: 'text', text: truncateIfNeeded(text) }] };\n },\n },\n {\n name: 'get_ticket_attachments',\n namespace: 'tickets',\n readOnly: true,\n title: 'Get Zendesk Ticket Attachments',\n description:\n 'Retrieve ticket attachments. Images are embedded inline; other files are listed as text references.',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n attachment_ids: z\n .array(z.number().int())\n .optional()\n .describe(\n 'Attachment IDs to fetch directly (e.g. extracted from a previous get_ticket(include_comments=true) call). When provided, skips the comments fetch entirely. When omitted, all attachments of the ticket are returned.',\n ),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, attachment_ids } = params as {\n ticket_id: number;\n attachment_ids?: number[];\n };\n const token = await getToken();\n\n let attachments: ZendeskTicketAttachment[];\n if (attachment_ids && attachment_ids.length > 0) {\n attachments = [];\n for (const id of attachment_ids) {\n try {\n const { attachment } = await zendeskGet<{ attachment: ZendeskTicketAttachment }>(\n subdomain,\n token,\n `/attachments/${id}`,\n );\n attachments.push(attachment);\n } catch (error) {\n if (!(error instanceof ZendeskApiError) || error.status !== 404) throw error;\n }\n }\n } else {\n const comments = await fetchAllTicketComments(subdomain, token, ticket_id);\n attachments = comments.flatMap((c) => c.attachments ?? []);\n }\n\n if (attachments.length === 0) {\n return {\n content: [{ type: 'text', text: `No attachments found on ticket #${ticket_id}.` }],\n };\n }\n const blocks = await collectAttachmentBlocks(subdomain, token, attachments);\n return {\n content: [\n {\n type: 'text',\n text: `# Attachments for ticket #${ticket_id} (${attachments.length} total)`,\n },\n ...blocks,\n ],\n };\n },\n },\n {\n name: 'search_tickets',\n namespace: 'tickets',\n readOnly: true,\n title: 'Search Zendesk Tickets',\n description:\n 'Search tickets using Zendesk query syntax (e.g., \"status:open assignee:me\", \"priority:urgent type:incident\"). Returns total count.',\n inputSchema: z.object({\n query: z.string().min(1).describe('Zendesk search query string'),\n per_page: z\n .number()\n .int()\n .min(1)\n .max(MAX_PAGE_SIZE)\n .default(DEFAULT_PAGE_SIZE)\n .describe('Results per page'),\n page: z.number().int().min(1).default(1).describe('Page number'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { query, per_page, page } = params as {\n query: string;\n per_page: number;\n page: number;\n };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<ZendeskTicket>>(\n subdomain,\n token,\n '/search',\n {\n query: `type:ticket ${query}`,\n ...buildOffsetParams(per_page, page),\n },\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.results ?? [],\n formatTicket,\n extractSearchPaginationMeta(response, per_page, page),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'create_ticket',\n namespace: 'tickets',\n readOnly: false,\n title: 'Create Zendesk Ticket',\n description:\n 'Create a new Zendesk support ticket with subject, description, and optional priority/type/assignee/tags.',\n inputSchema: z.object({\n subject: z.string().min(1).describe('Ticket subject'),\n description: z.string().min(1).describe('Ticket description'),\n priority: z.enum(['urgent', 'high', 'normal', 'low']).optional(),\n type: z.enum(['problem', 'incident', 'question', 'task']).optional(),\n assignee_id: z.number().int().optional(),\n group_id: z.number().int().optional(),\n tags: z.array(z.string()).optional(),\n custom_fields: z.array(z.object({ id: z.number().int(), value: z.unknown() })).optional(),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { subject, description, ...rest } = params as Record<string, unknown>;\n const token = await getToken();\n const { ticket } = await zendeskPost<{ ticket: ZendeskTicket }>(\n subdomain,\n token,\n '/tickets',\n {\n ticket: { subject, comment: { body: description }, ...rest },\n },\n );\n return {\n content: [\n { type: 'text', text: `Ticket #${ticket.id} created.\\n\\n${formatTicket(ticket)}` },\n ],\n };\n },\n },\n {\n name: 'update_ticket',\n namespace: 'tickets',\n readOnly: false,\n title: 'Update Zendesk Ticket',\n description:\n 'Update an existing ticket (status, priority, type, assignee, group, subject, tags, custom fields).',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n status: z.enum(['new', 'open', 'pending', 'hold', 'solved', 'closed']).optional(),\n priority: z.enum(['urgent', 'high', 'normal', 'low']).optional(),\n type: z.enum(['problem', 'incident', 'question', 'task']).optional(),\n assignee_id: z.number().int().optional(),\n group_id: z.number().int().optional(),\n subject: z.string().optional(),\n tags: z.array(z.string()).optional(),\n custom_fields: z.array(z.object({ id: z.number().int(), value: z.unknown() })).optional(),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, ...updates } = params as { ticket_id: number } & Record<string, unknown>;\n const token = await getToken();\n const { ticket } = await zendeskPut<{ ticket: ZendeskTicket }>(\n subdomain,\n token,\n `/tickets/${ticket_id}`,\n { ticket: updates },\n );\n return {\n content: [\n { type: 'text', text: `Ticket #${ticket.id} updated.\\n\\n${formatTicket(ticket)}` },\n ],\n };\n },\n },\n {\n name: 'add_private_note',\n namespace: 'tickets',\n readOnly: false,\n title: 'Add Private Note',\n description: 'Add an internal note (not visible to requester) to a ticket.',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n body: z.string().min(1).describe('Note content'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, body } = params as { ticket_id: number; body: string };\n const token = await getToken();\n await zendeskPut(subdomain, token, `/tickets/${ticket_id}`, {\n ticket: { comment: { body, public: false } },\n });\n return { content: [{ type: 'text', text: `Private note added to ticket #${ticket_id}.` }] };\n },\n },\n {\n name: 'add_public_comment',\n namespace: 'tickets',\n readOnly: false,\n title: 'Add Public Comment',\n description: 'Add a public comment (visible to requester) to a ticket.',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n body: z.string().min(1).describe('Comment content'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, body } = params as { ticket_id: number; body: string };\n const token = await getToken();\n await zendeskPut(subdomain, token, `/tickets/${ticket_id}`, {\n ticket: { comment: { body, public: true } },\n });\n return {\n content: [{ type: 'text', text: `Public comment added to ticket #${ticket_id}.` }],\n };\n },\n },\n {\n name: 'list_tickets',\n namespace: 'tickets',\n readOnly: true,\n title: 'List Zendesk Tickets',\n description: 'List tickets with cursor-based pagination, sorted by most recently updated.',\n inputSchema: z.object({\n page_size: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),\n cursor: z.string().optional().describe('Pagination cursor'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { page_size, cursor } = params as { page_size: number; cursor?: string };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<ZendeskTicket>>(\n subdomain,\n token,\n '/tickets',\n buildCursorParams(page_size, cursor),\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.tickets ?? [],\n formatTicket,\n extractPaginationMeta(response),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'get_linked_incidents',\n namespace: 'tickets',\n readOnly: true,\n title: 'Get Linked Incidents',\n description: 'Get all incident tickets linked to a problem ticket.',\n inputSchema: z.object({\n problem_id: z.number().int().describe('Problem ticket ID'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { problem_id } = params as { problem_id: number };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<ZendeskTicket>>(\n subdomain,\n token,\n `/tickets/${problem_id}/incidents`,\n );\n const incidents = response.tickets ?? [];\n const text =\n incidents.length > 0\n ? `# Incidents linked to problem #${problem_id}\\n\\n${incidents.map(formatTicket).join('\\n\\n')}`\n : `No incidents linked to problem #${problem_id}.`;\n return { content: [{ type: 'text', text: truncateIfNeeded(text) }] };\n },\n },\n {\n name: 'manage_tags',\n namespace: 'tickets',\n readOnly: false,\n title: 'Manage Ticket Tags',\n description: 'Add or remove tags on a ticket.',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n add: z.array(z.string()).optional().describe('Tags to add'),\n remove: z.array(z.string()).optional().describe('Tags to remove'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, add, remove } = params as {\n ticket_id: number;\n add?: string[];\n remove?: string[];\n };\n const token = await getToken();\n const { ticket } = await zendeskGet<{ ticket: ZendeskTicket }>(\n subdomain,\n token,\n `/tickets/${ticket_id}`,\n );\n const tags = new Set(ticket.tags);\n add?.forEach((t) => {\n tags.add(t);\n });\n remove?.forEach((t) => {\n tags.delete(t);\n });\n const { ticket: updated } = await zendeskPut<{ ticket: ZendeskTicket }>(\n subdomain,\n token,\n `/tickets/${ticket_id}`,\n { ticket: { tags: [...tags] } },\n );\n return {\n content: [\n {\n type: 'text',\n text: `Tags updated on ticket #${ticket_id}. Current: ${updated.tags.join(', ') || 'none'}`,\n },\n ],\n };\n },\n },\n ];\n};\n","import * as z from 'zod/v4';\nimport { zendeskGet } from '../client/zendesk-api';\nimport { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE } from '../constants';\nimport type { ZendeskListResponse, ZendeskOrganization, ZendeskUser } from '../types';\nimport { formatList, formatOrganization, formatUser } from '../utils/formatting';\nimport {\n buildCursorParams,\n buildOffsetParams,\n extractPaginationMeta,\n extractSearchPaginationMeta,\n} from '../utils/pagination';\nimport type { ToolContext, ToolDefinition } from './definitions';\n\nexport const createUserTools = (ctx: ToolContext): ToolDefinition[] => {\n const { subdomain, getToken } = ctx;\n\n return [\n {\n name: 'get_current_user',\n namespace: 'users',\n readOnly: true,\n title: 'Get Current Zendesk User',\n description:\n 'Get the currently authenticated Zendesk user. Useful to verify identity and permissions.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async () => {\n const token = await getToken();\n const { user } = await zendeskGet<{ user: ZendeskUser }>(subdomain, token, '/users/me');\n return { content: [{ type: 'text', text: formatUser(user) }] };\n },\n },\n {\n name: 'search_users',\n namespace: 'users',\n readOnly: true,\n title: 'Search Zendesk Users',\n description:\n 'Search for users by name, email, or other criteria using Zendesk search query syntax. Returns total count.',\n inputSchema: z.object({\n query: z.string().min(1).describe('Search query'),\n per_page: z\n .number()\n .int()\n .min(1)\n .max(MAX_PAGE_SIZE)\n .default(DEFAULT_PAGE_SIZE)\n .describe('Results per page'),\n page: z.number().int().min(1).default(1).describe('Page number'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { query, per_page, page } = params as {\n query: string;\n per_page: number;\n page: number;\n };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<ZendeskUser>>(\n subdomain,\n token,\n '/search',\n {\n query: `type:user ${query}`,\n ...buildOffsetParams(per_page, page),\n },\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.results ?? [],\n formatUser,\n extractSearchPaginationMeta(response, per_page, page),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'get_user',\n namespace: 'users',\n readOnly: true,\n title: 'Get Zendesk User',\n description: 'Retrieve a user by ID.',\n inputSchema: z.object({ user_id: z.number().int().describe('User ID') }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { user_id } = params as { user_id: number };\n const token = await getToken();\n const { user } = await zendeskGet<{ user: ZendeskUser }>(\n subdomain,\n token,\n `/users/${user_id}`,\n );\n return { content: [{ type: 'text', text: formatUser(user) }] };\n },\n },\n {\n name: 'get_organization',\n namespace: 'users',\n readOnly: true,\n title: 'Get Zendesk Organization',\n description: 'Retrieve an organization by ID.',\n inputSchema: z.object({ organization_id: z.number().int().describe('Organization ID') }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { organization_id } = params as { organization_id: number };\n const token = await getToken();\n const { organization } = await zendeskGet<{ organization: ZendeskOrganization }>(\n subdomain,\n token,\n `/organizations/${organization_id}`,\n );\n return { content: [{ type: 'text', text: formatOrganization(organization) }] };\n },\n },\n {\n name: 'list_organizations',\n namespace: 'users',\n readOnly: true,\n title: 'List Zendesk Organizations',\n description: 'List all organizations with pagination.',\n inputSchema: z.object({\n page_size: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),\n cursor: z.string().optional(),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { page_size, cursor } = params as { page_size: number; cursor?: string };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<ZendeskOrganization>>(\n subdomain,\n token,\n '/organizations',\n buildCursorParams(page_size, cursor),\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.organizations ?? [],\n formatOrganization,\n extractPaginationMeta(response),\n ),\n },\n ],\n };\n },\n },\n ];\n};\n","import type { ToolContext, ToolDefinition } from './definitions';\nimport { createHelpCenterTools } from './help-center';\nimport { createSearchTools } from './search';\nimport { createTicketTools } from './tickets';\nimport { createUserTools } from './users';\n\nexport type { ToolContext, ToolDefinition } from './definitions';\n\nexport const createAllTools = (ctx: ToolContext): ToolDefinition[] => [\n ...createTicketTools(ctx),\n ...createSearchTools(ctx),\n ...createHelpCenterTools(ctx),\n ...createUserTools(ctx),\n];\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport * as z from 'zod/v4';\nimport { ZendeskApiError } from './client/zendesk-api';\nimport type { Config } from './config';\nimport { filterTools, groupByNamespace } from './routing/registry';\nimport type { ToolAnnotations, ToolResult } from './tools/definitions';\nimport { createAllTools, type ToolDefinition } from './tools/index';\nimport { type Logger, silentLogger } from './utils/logger';\nimport { readPackageInfo } from './utils/package-info';\n\n/**\n * Invoke a tool handler, notifying `onUnauthorized` when Zendesk rejects the\n * token (401). This lets the OAuth store drop the dead token so the next call\n * refreshes/re-authenticates instead of replaying a revoked token. A no-op\n * callback (API-token mode) leaves behavior unchanged.\n */\nconst runHandler = async (\n def: ToolDefinition,\n params: Record<string, unknown>,\n onUnauthorized: (() => void) | undefined,\n): Promise<ToolResult> => {\n try {\n return await def.handler(params);\n } catch (err) {\n if (onUnauthorized && err instanceof ZendeskApiError && err.status === 401) {\n onUnauthorized();\n }\n throw err;\n }\n};\n\nconst NAMESPACE_LABELS: Record<string, { toolName: string; title: string }> = {\n tickets: { toolName: 'zendesk_tickets', title: 'Zendesk Tickets' },\n help_center: { toolName: 'zendesk_help_center', title: 'Zendesk Help Center' },\n users: { toolName: 'zendesk_users', title: 'Zendesk Users' },\n};\n\n// Keep proxy descriptions compact: a proxy tool concatenates one line per\n// sub-operation, so only the first sentence of each tool description is\n// included. Clients still receive the full schema via the wrapped tool.\nexport const summarizeDescription = (description: string): string => {\n const idx = description.indexOf('. ');\n if (idx === -1) return description;\n return description.slice(0, idx + 1);\n};\n\nexport const buildOperationList = (\n tools: ReadonlyArray<Pick<ToolDefinition, 'name' | 'description' | 'readOnly'>>,\n): string =>\n tools\n .map(\n (t) =>\n `- **${t.name}**: ${summarizeDescription(t.description)}${t.readOnly ? '' : ' (write)'}`,\n )\n .join('\\n');\n\n// A proxy aggregates N sub-operations. Hints follow the safest plausible\n// reading: readOnly/idempotent only if EVERY op is, destructive as soon as\n// ANY op is. openWorld is always true (we always hit Zendesk).\n// Mistral/Vibe ignore annotations entirely, hence the `[RO]` prefix below.\nexport const aggregateAnnotations = (\n tools: ReadonlyArray<Pick<ToolDefinition, 'annotations'>>,\n): ToolAnnotations => ({\n readOnlyHint: tools.every((t) => t.annotations.readOnlyHint),\n destructiveHint: tools.some((t) => t.annotations.destructiveHint),\n idempotentHint: tools.every((t) => t.annotations.idempotentHint),\n openWorldHint: true,\n});\n\ntype ProxyDispatch = (args: Record<string, unknown>) => Promise<ToolResult>;\n\n// Each proxy carries its OWN handler map, scoped to the operations it\n// advertises. In `namespace` mode this is essential: without it, a caller\n// could invoke `zendesk_tickets` with operation=\"get_article\" and dispatch\n// a help-center handler via a shared global map. The description would lie\n// but the call would still succeed.\nexport const buildProxyDispatch = (\n tools: ToolDefinition[],\n onUnauthorized: (() => void) | undefined,\n): ProxyDispatch => {\n const operationNames = tools.map((t) => t.name);\n const localHandlers = new Map<string, ToolDefinition>(tools.map((t) => [t.name, t]));\n\n return async (args) => {\n const { operation, params } = args as {\n operation: string;\n params: Record<string, unknown>;\n };\n const def = localHandlers.get(operation);\n if (!def) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Unknown operation \"${operation}\". Available: ${operationNames.join(', ')}`,\n },\n ],\n };\n }\n const validated = def.inputSchema.parse(params);\n return runHandler(def, validated, onUnauthorized);\n };\n};\n\nconst registerProxyTool = (\n server: McpServer,\n toolName: string,\n title: string,\n tools: ToolDefinition[],\n readOnlyMode: boolean,\n onUnauthorized: (() => void) | undefined,\n): void => {\n const operationNames = tools.map((t) => t.name);\n const operationList = buildOperationList(tools);\n const annotations = aggregateAnnotations(tools);\n const prefix = readOnlyMode ? '[RO] ' : '';\n\n const dispatch = buildProxyDispatch(tools, onUnauthorized);\n\n server.registerTool(\n toolName,\n {\n title,\n description: `${prefix}${title}. Specify the operation and its parameters.\\n\\nAvailable operations:\\n${operationList}`,\n inputSchema: {\n operation: z.string().describe(`One of: ${operationNames.join(', ')}`),\n params: z.record(z.string(), z.unknown()).default({}).describe('Operation parameters'),\n },\n annotations,\n },\n async (args) => dispatch(args as Record<string, unknown>),\n );\n};\n\nexport const createMcpServer = (\n config: Config,\n getToken: () => string | Promise<string>,\n logger: Logger = silentLogger,\n // Called when a tool handler hits a 401 from Zendesk (OAuth mode only). Lets\n // the token store invalidate the rejected token. Omitted in API-token mode.\n onUnauthorized?: () => void,\n): McpServer => {\n // Read name/version from package.json at runtime rather than hardcoding them\n // (the old literals were stale and even carried the wrong package name).\n const pkg = readPackageInfo();\n const server = new McpServer(\n {\n name: pkg.name,\n version: pkg.version,\n },\n // Advertise the logging capability so structured diagnostics (notably the\n // OAuth browser flow) reach clients that support it. Clients that don't\n // simply ignore the notifications.\n { capabilities: { logging: {} } },\n );\n\n // Route the logger's MCP sink through this server. Auth runs lazily on the\n // first tool call (after connect), so notifications can flow by then.\n logger.attachServer(server);\n\n const allTools = createAllTools({ subdomain: config.subdomain, getToken });\n\n // Apply filters (--read-only, --namespace, --tool)\n const filteredTools = filterTools(allTools, {\n readOnly: config.readOnly,\n namespaces: config.namespaces,\n tools: config.tools,\n });\n\n switch (config.mode) {\n case 'all': {\n for (const tool of filteredTools) {\n server.registerTool(\n tool.name,\n {\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema.shape,\n annotations: tool.annotations,\n },\n async (params) => runHandler(tool, params as Record<string, unknown>, onUnauthorized),\n );\n }\n break;\n }\n case 'namespace': {\n const grouped = groupByNamespace(filteredTools);\n for (const [namespace, tools] of grouped) {\n const label = NAMESPACE_LABELS[namespace];\n if (label) {\n registerProxyTool(\n server,\n label.toolName,\n label.title,\n tools,\n config.readOnly,\n onUnauthorized,\n );\n }\n }\n break;\n }\n case 'single': {\n registerProxyTool(\n server,\n 'zendesk',\n 'Zendesk',\n filteredTools,\n config.readOnly,\n onUnauthorized,\n );\n break;\n }\n }\n\n logger.info('tools_registered', { count: filteredTools.length, mode: config.mode });\n return server;\n};\n","import { randomUUID } from 'node:crypto';\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport type { Config } from '../config';\nimport { getOAuthUrls } from '../constants';\nimport { createMcpServer } from '../server';\nimport { type Logger, silentLogger } from '../utils/logger';\n\nconst WILDCARD_HOSTS = new Set(['0.0.0.0', '::', '*']);\n\n// Default CORS allowlist: the major web MCP clients that work today via\n// Custom Connector UIs. Native clients (Claude Desktop, Claude Code CLI,\n// Cursor, VS Code, Zed) send no Origin header — they're unaffected. Extend\n// the list with --cors-origin / CORS_ORIGIN per deployment.\n//\n// Ordered by current usage share (ChatGPT first). Update over time as the\n// MCP client landscape evolves.\nexport const DEFAULT_BROWSER_MCP_CLIENT_ORIGINS: ReadonlyArray<string> = [\n 'https://chatgpt.com',\n 'https://chat.openai.com',\n 'https://claude.ai',\n 'https://gemini.google.com',\n 'https://copilot.microsoft.com',\n 'https://www.perplexity.ai',\n 'https://chat.mistral.ai',\n 'https://grok.com',\n];\n\n// Methods + headers exposed across CORS for the /mcp endpoint. Headers list\n// covers what the SDK's StreamableHTTPClientTransport sets plus the bearer.\nconst CORS_ALLOWED_METHODS = 'GET, POST, DELETE, OPTIONS';\nconst CORS_ALLOWED_HEADERS =\n 'Authorization, Content-Type, Accept, mcp-session-id, mcp-protocol-version, last-event-id';\n// Clients need to read the session ID from the response to send it on\n// subsequent requests; without this header they can't.\nconst CORS_EXPOSE_HEADERS = 'mcp-session-id';\nconst CORS_MAX_AGE = '600';\n\nconst LOCALHOST_HOSTNAMES = new Set(['localhost', '127.0.0.1', '[::1]']);\nconst ALLOWED_PROTOCOLS = new Set(['http:', 'https:']);\n\n/**\n * Returns the origin string to reflect in `Access-Control-Allow-Origin`, or\n * `undefined` if the origin is not allowed.\n *\n * The returned value is **never the raw `Origin` request header**. It comes\n * from one of three sanitization points:\n *\n * 1. An entry of the hardcoded `DEFAULT_BROWSER_MCP_CLIENT_ORIGINS` array.\n * 2. An entry of the operator-configured `extraOrigins` array.\n * 3. A loopback origin rebuilt from validated URL components after the\n * hostname has been allowlisted against `LOCALHOST_HOSTNAMES`.\n *\n * This shape keeps the dataflow from request header to response header\n * gated by a constant allowlist, which is the pattern CodeQL's\n * `js/cors-misconfiguration-for-credentials` rule recognises as safe when\n * combined with `Access-Control-Allow-Credentials: true`.\n */\nexport const resolveAllowedOrigin = (\n origin: string,\n extraOrigins: ReadonlyArray<string> | undefined,\n): string | undefined => {\n const defaultMatch = DEFAULT_BROWSER_MCP_CLIENT_ORIGINS.find((entry) => entry === origin);\n if (defaultMatch) return defaultMatch;\n\n const extraMatch = extraOrigins?.find((entry) => entry === origin);\n if (extraMatch) return extraMatch;\n\n // Loopback on any port: parse, allowlist the hostname, then rebuild from\n // the validated parts. The output never includes raw header bytes.\n try {\n const url = new URL(origin);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) return undefined;\n if (!LOCALHOST_HOSTNAMES.has(url.hostname)) return undefined;\n const port = url.port || (url.protocol === 'https:' ? '443' : '80');\n return `${url.protocol}//${url.hostname}:${port}`;\n } catch {\n return undefined;\n }\n};\n\nconst applyCorsHeaders = (\n req: IncomingMessage,\n res: ServerResponse,\n extraOrigins: ReadonlyArray<string>,\n): void => {\n const requestOrigin = req.headers['origin'];\n if (typeof requestOrigin !== 'string' || requestOrigin.length === 0) return;\n\n // The value passed to setHeader is the matched entry from the constant\n // allowlist (or a loopback origin rebuilt from validated URL parts) —\n // never the raw request header. This satisfies CodeQL's\n // `js/cors-misconfiguration-for-credentials` rule when combined with\n // Access-Control-Allow-Credentials: true.\n const allowedOrigin = resolveAllowedOrigin(requestOrigin, extraOrigins);\n if (!allowedOrigin) return;\n\n res.setHeader('Access-Control-Allow-Origin', allowedOrigin);\n res.setHeader('Vary', 'Origin');\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n res.setHeader('Access-Control-Expose-Headers', CORS_EXPOSE_HEADERS);\n};\n\nconst handleCorsPreflight = (\n req: IncomingMessage,\n res: ServerResponse,\n extraOrigins: ReadonlyArray<string>,\n): boolean => {\n if (req.method !== 'OPTIONS') return false;\n applyCorsHeaders(req, res, extraOrigins);\n // Preflight needs Allow-Methods and Allow-Headers in addition to the basic\n // CORS headers; if the origin isn't allowlisted, applyCorsHeaders is a\n // no-op and we still 204 (the browser blocks based on missing ACAO).\n if (res.getHeader('Access-Control-Allow-Origin')) {\n res.setHeader('Access-Control-Allow-Methods', CORS_ALLOWED_METHODS);\n res.setHeader('Access-Control-Allow-Headers', CORS_ALLOWED_HEADERS);\n res.setHeader('Access-Control-Max-Age', CORS_MAX_AGE);\n }\n res.writeHead(204);\n res.end();\n return true;\n};\n\n// Build the canonical `resource` URL we advertise in the OAuth metadata.\n// Precedence:\n// 1. Explicit --public-url / PUBLIC_URL (operators behind a reverse proxy\n// must set this; Azure App Service: PUBLIC_URL=\"https://${WEBSITE_HOSTNAME}\").\n// 2. host:port when host is a real, routable hostname or IP (not the bind\n// wildcard 0.0.0.0 / :: / unspecified).\n// 3. Fallback to host:port + warning. Clients following RFC 8707 strictly\n// will reject this resource identifier, so log a clear warning so the\n// operator knows to set PUBLIC_URL.\nexport const resolveResourceUrl = (config: Config, logger: Logger = silentLogger): string => {\n if (config.publicUrl) return config.publicUrl.replace(/\\/+$/, '');\n if (!WILDCARD_HOSTS.has(config.host)) {\n return `http://${config.host}:${config.port}`;\n }\n logger.warn('public_url_unset', {\n host: config.host,\n advertised: `http://${config.host}:${config.port}`,\n hint:\n 'OAuth discovery will advertise a non-routable resource identifier and spec-compliant ' +\n 'MCP clients may refuse the connection. Set PUBLIC_URL (or --public-url) to the URL ' +\n 'clients use to reach this server (e.g. https://your-host.example.com).',\n });\n return `http://${config.host}:${config.port}`;\n};\n\n// Error message must stay ASCII-only: it gets embedded in the WWW-Authenticate\n// response header on 401, and node:http's setHeader rejects non-ASCII bytes\n// with ERR_INVALID_CHAR (which would surface as a 500 instead of the\n// spec-required 401).\nconst MISSING_BEARER_MESSAGE =\n 'Missing Authorization: Bearer <zendesk-oauth-token> header. ' +\n 'HTTP mode requires per-user OAuth 2.1 PKCE - obtain a token from Zendesk via your MCP client.';\n\nexport const extractBearer = (request: IncomingMessage): string | undefined => {\n const header = request.headers['authorization'];\n if (typeof header !== 'string') return undefined;\n if (!header.toLowerCase().startsWith('bearer ')) return undefined;\n return header.slice('bearer '.length).trim();\n};\n\ninterface OAuthMetadata {\n protectedResource: {\n authorization_servers: string[];\n resource: string;\n bearer_methods_supported: string[];\n scopes_supported: string[];\n };\n authorizationServer: {\n issuer: string;\n authorization_endpoint: string;\n token_endpoint: string;\n response_types_supported: string[];\n grant_types_supported: string[];\n code_challenge_methods_supported: string[];\n token_endpoint_auth_methods_supported: string[];\n scopes_supported: string[];\n };\n}\n\nexport const buildOAuthMetadata = (\n config: Config,\n logger: Logger = silentLogger,\n): OAuthMetadata => {\n const { authorizeUrl, tokenUrl } = getOAuthUrls(config.subdomain);\n const issuer = `https://${config.subdomain}.zendesk.com`;\n const resource = resolveResourceUrl(config, logger);\n return {\n protectedResource: {\n authorization_servers: [issuer],\n resource,\n bearer_methods_supported: ['header'],\n scopes_supported: ['read', 'write'],\n },\n authorizationServer: {\n issuer,\n authorization_endpoint: authorizeUrl,\n token_endpoint: tokenUrl,\n response_types_supported: ['code'],\n grant_types_supported: ['authorization_code', 'refresh_token'],\n code_challenge_methods_supported: ['S256'],\n token_endpoint_auth_methods_supported: ['none'],\n scopes_supported: ['read', 'write'],\n },\n };\n};\n\nconst sendJson = (res: ServerResponse, status: number, body: unknown): void => {\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(body));\n};\n\n// Single construction point for JSON-RPC-shaped HTTP errors so the envelope\n// ({ error, id: null, jsonrpc }) can't drift between the 4xx/5xx paths.\nconst sendJsonRpcError = (\n res: ServerResponse,\n status: number,\n code: number,\n message: string,\n headers: Record<string, string> = {},\n): void => {\n res.writeHead(status, { 'Content-Type': 'application/json', ...headers });\n res.end(JSON.stringify({ error: { code, message }, id: null, jsonrpc: '2.0' }));\n};\n\nconst sendUnauthorized = (res: ServerResponse, resource: string): void => {\n // RFC 6750 + MCP 2025-06-18: the WWW-Authenticate header points the client\n // at the resource metadata that bootstraps the OAuth discovery flow.\n const wwwAuthenticate = `Bearer resource_metadata=\"${resource}/.well-known/oauth-protected-resource\", error=\"invalid_token\", error_description=\"${MISSING_BEARER_MESSAGE}\"`;\n sendJsonRpcError(res, 401, -32000, MISSING_BEARER_MESSAGE, {\n 'WWW-Authenticate': wwwAuthenticate,\n });\n};\n\ninterface Session {\n transport: StreamableHTTPServerTransport;\n /**\n * Latest bearer presented for this session. Tool calls read through this\n * object so a client that refreshes its Zendesk token mid-session has the\n * fresh token used on the next call.\n */\n auth: { bearer: string };\n /** Epoch ms of the last request routed to this session (idle eviction). */\n lastActivityAt: number;\n close(): Promise<void>;\n}\n\n// JSON-RPC tool calls are small; this is a generous ceiling that exists only\n// so a client can't stream an unbounded body into server memory.\nexport const MAX_BODY_BYTES = 4 * 1024 * 1024;\n\ntype BodyResult =\n | { ok: true; value: unknown }\n | { ok: false; status: number; rpcCode: number; message: string };\n\n// Read and parse the request body. The SDK transport could parse the stream\n// itself, but it neither caps the body size nor maps parse failures to the\n// right HTTP status — buffering here keeps both concerns in one place.\nconst readJsonBody = (req: IncomingMessage, maxBodyBytes: number): Promise<BodyResult> =>\n new Promise((resolve) => {\n const chunks: Buffer[] = [];\n let total = 0;\n let settled = false;\n const settle = (result: BodyResult): void => {\n if (settled) return;\n settled = true;\n resolve(result);\n };\n req.on('data', (chunk: Buffer) => {\n total += chunk.length;\n if (total > maxBodyBytes) {\n req.removeAllListeners('data');\n settle({\n ok: false,\n status: 413,\n rpcCode: -32600,\n message: `Request body exceeds ${maxBodyBytes} bytes.`,\n });\n return;\n }\n chunks.push(chunk);\n });\n req.on('end', () => {\n const raw = Buffer.concat(chunks).toString('utf8');\n if (!raw) {\n settle({ ok: true, value: undefined });\n return;\n }\n try {\n settle({ ok: true, value: JSON.parse(raw) });\n } catch {\n settle({\n ok: false,\n status: 400,\n rpcCode: -32700,\n message: 'Parse error: request body is not valid JSON.',\n });\n }\n });\n req.on('error', () =>\n settle({\n ok: false,\n status: 400,\n rpcCode: -32600,\n message: 'Request body could not be read.',\n }),\n );\n });\n\nconst respondBodyError = (\n req: IncomingMessage,\n res: ServerResponse,\n failure: Extract<BodyResult, { ok: false }>,\n): void => {\n const headers = failure.status === 413 ? { Connection: 'close' } : {};\n sendJsonRpcError(res, failure.status, failure.rpcCode, failure.message, headers);\n if (failure.status === 413) {\n // The client may still be mid-upload with the request stream paused\n // (readJsonBody dropped its listeners at the cap). Left alone, the\n // connection wedges: the client blocks on backpressure and\n // httpServer.close() waits on the socket forever. Drop it as soon as the\n // 413 has flushed.\n if (res.writableFinished) req.destroy();\n else res.once('finish', () => req.destroy());\n }\n};\n\n// A client that vanishes without DELETEing its session would otherwise leak a\n// McpServer + bearer forever (the SDK only fires onclose on an explicit\n// DELETE). A periodic sweep closes sessions with no traffic for this long;\n// well-behaved clients simply re-initialize on their next request.\nconst SESSION_IDLE_TIMEOUT_MS = 30 * 60 * 1000;\nconst SESSION_SWEEP_INTERVAL_MS = 60 * 1000;\n\nexport interface HttpServerHandle {\n /** Actual bound port (resolved when listen completes — useful for port:0 tests). */\n port: number;\n /** Stop the HTTP server and tear down every active MCP session. */\n close(): Promise<void>;\n}\n\nexport interface HttpTransportOptions {\n /** Idle-session eviction timeout override (tests). */\n sessionIdleTimeoutMs?: number;\n /** Idle-session sweep interval override (tests). */\n sweepIntervalMs?: number;\n /** Request body cap override (tests use a small cap to stay cheap). */\n maxBodyBytes?: number;\n}\n\nexport const startHttpTransport = async (\n config: Config,\n logger: Logger = silentLogger,\n options: HttpTransportOptions = {},\n): Promise<HttpServerHandle> => {\n const metadata = buildOAuthMetadata(config, logger);\n const sessions = new Map<string, Session>();\n const idleTimeoutMs = options.sessionIdleTimeoutMs ?? SESSION_IDLE_TIMEOUT_MS;\n const maxBodyBytes = options.maxBodyBytes ?? MAX_BODY_BYTES;\n\n const handleMcpRequest = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n // MCP 2025-06-18: the Authorization header is validated on EVERY request,\n // not only at session initialization — a session id alone is not a\n // credential. An unauthenticated request gets a 401 + WWW-Authenticate\n // that bootstraps the OAuth flow on the client side.\n const bearer = extractBearer(req);\n if (!bearer) {\n sendUnauthorized(res, metadata.protectedResource.resource);\n return;\n }\n\n const sessionId =\n typeof req.headers['mcp-session-id'] === 'string' ? req.headers['mcp-session-id'] : undefined;\n\n // Existing session: adopt the presented bearer (clients may have\n // refreshed their Zendesk token mid-session) and route the request to\n // the session's transport.\n if (sessionId) {\n const session = sessions.get(sessionId);\n if (session) {\n session.auth.bearer = bearer;\n session.lastActivityAt = Date.now();\n const body =\n req.method === 'POST'\n ? await readJsonBody(req, maxBodyBytes)\n : ({ ok: true, value: undefined } as const);\n if (!body.ok) {\n respondBodyError(req, res, body);\n return;\n }\n await session.transport.handleRequest(req, res, body.value);\n return;\n }\n }\n\n if (req.method !== 'POST') {\n // Non-POST without a session ID can't initialize a new session.\n sendJsonRpcError(res, 400, -32000, 'No active session; initialize via POST first.');\n return;\n }\n\n const body = await readJsonBody(req, maxBodyBytes);\n if (!body.ok) {\n respondBodyError(req, res, body);\n return;\n }\n\n // New session: the bearer is held in a per-session mutable cell read by\n // the per-session McpServer's token source — no async-local storage\n // needed because each session has its own server instance.\n const auth = { bearer };\n const server = createMcpServer(config, () => auth.bearer, logger);\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n onsessioninitialized: (newId) => {\n sessions.set(newId, {\n transport,\n auth,\n lastActivityAt: Date.now(),\n close: async () => {\n await transport.close();\n await server.close();\n },\n });\n },\n });\n transport.onclose = () => {\n if (transport.sessionId) sessions.delete(transport.sessionId);\n };\n // The SDK's Transport interface uses `onclose?: () => void` which conflicts\n // with the concrete transport's class member typing under\n // exactOptionalPropertyTypes — the runtime contract is identical, this is\n // purely a type-system seam.\n // biome-ignore lint/suspicious/noExplicitAny: SDK type seam, see above\n await server.connect(transport as any);\n await transport.handleRequest(req, res, body.value);\n };\n\n const requestListener = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n try {\n // CORS preflight is short-circuited before routing; for actual requests\n // we attach the allow-headers BEFORE we dispatch so they ride along\n // with the response regardless of which handler ends it.\n if (handleCorsPreflight(req, res, config.corsOrigins)) return;\n applyCorsHeaders(req, res, config.corsOrigins);\n\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n\n if (url.pathname === '/.well-known/oauth-protected-resource' && req.method === 'GET') {\n sendJson(res, 200, metadata.protectedResource);\n return;\n }\n if (url.pathname === '/.well-known/oauth-authorization-server' && req.method === 'GET') {\n sendJson(res, 200, metadata.authorizationServer);\n return;\n }\n if (url.pathname === '/healthz' && req.method === 'GET') {\n sendJson(res, 200, { status: 'ok', subdomain: config.subdomain });\n return;\n }\n if (url.pathname === '/mcp') {\n await handleMcpRequest(req, res);\n return;\n }\n\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Not found', path: url.pathname }));\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Internal Server Error';\n if (!res.headersSent) {\n sendJsonRpcError(res, 500, -32603, message);\n } else if (!res.writableEnded) {\n // Headers already on the wire: appending a JSON error mid-stream would\n // corrupt the body; just terminate the response.\n res.end();\n }\n }\n };\n\n const httpServer: Server = createServer((req, res) => {\n void requestListener(req, res);\n });\n\n await new Promise<void>((resolve, reject) => {\n httpServer.once('error', reject);\n httpServer.listen(config.port, config.host, () => {\n httpServer.off('error', reject);\n resolve();\n });\n });\n\n const addr = httpServer.address();\n const boundPort = typeof addr === 'object' && addr !== null ? addr.port : config.port;\n logger.info('http_transport_ready', { host: config.host, port: boundPort });\n\n const sweepIdleSessions = async (): Promise<void> => {\n const cutoff = Date.now() - idleTimeoutMs;\n for (const [id, session] of sessions) {\n if (session.lastActivityAt > cutoff) continue;\n sessions.delete(id);\n try {\n await session.close();\n } catch (err) {\n logger.warn('session_close_failed', {\n sessionId: id,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n };\n const sweeper = setInterval(\n () => void sweepIdleSessions(),\n options.sweepIntervalMs ?? SESSION_SWEEP_INTERVAL_MS,\n );\n // The sweeper must never keep the process alive on its own.\n sweeper.unref();\n\n return {\n port: boundPort,\n close: async () => {\n clearInterval(sweeper);\n await Promise.all([...sessions.values()].map((session) => session.close()));\n sessions.clear();\n // server.close() waits for every connection to drain; a wedged or\n // keep-alive socket would stall shutdown forever. Sessions are already\n // closed at this point, so dropping the remaining sockets is safe.\n httpServer.closeAllConnections();\n await new Promise<void>((resolve, reject) => {\n httpServer.close((err) => (err ? reject(err) : resolve()));\n });\n },\n };\n};\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { type Logger, silentLogger } from '../utils/logger';\n\nexport const startStdioTransport = async (\n server: McpServer,\n logger: Logger = silentLogger,\n): Promise<void> => {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n logger.info('stdio_transport_ready');\n};\n","import { buildBasicAuthHeader } from './auth/api-token';\nimport { createTokenStore } from './auth/token-store';\nimport type { Config } from './config';\nimport { loadConfig } from './config';\nimport { createMcpServer } from './server';\nimport { startHttpTransport } from './transports/http';\nimport { startStdioTransport } from './transports/stdio';\nimport { createLogger, type Logger } from './utils/logger';\n\nconst buildStdioServer = (config: Config, logger: Logger) => {\n if (config.zendeskEmail && config.zendeskApiToken) {\n // API token mode — static Basic auth. No onUnauthorized callback: a stale\n // API token is a credential rotation problem, not something the server\n // can recover from at runtime.\n const staticToken = buildBasicAuthHeader(config.zendeskEmail, config.zendeskApiToken);\n return createMcpServer(config, () => staticToken, logger);\n }\n // OAuth mode — browser-based auth on first tool call. `invalidate` drops the\n // dead access token on a 401 so the next call refreshes/re-authenticates.\n const tokenStore = createTokenStore(\n {\n subdomain: config.subdomain,\n oauthClientId: config.oauthClientId,\n callbackPort: config.callbackPort,\n },\n logger,\n );\n return createMcpServer(config, tokenStore.getToken, logger, tokenStore.invalidate);\n};\n\nconst main = async (): Promise<void> => {\n const config = loadConfig();\n const logger = createLogger(config.logLevel);\n\n if (config.transport === 'stdio') {\n const server = buildStdioServer(config, logger);\n await startStdioTransport(server, logger);\n return;\n }\n\n // HTTP mode: the HTTP transport creates a per-session McpServer with the\n // request's bearer captured in its tools' closure — no shared state.\n await startHttpTransport(config, logger);\n};\n\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/auth/api-token.ts","../src/utils/logger.ts","../src/constants.ts","../src/auth/browser-oauth.ts","../src/utils/package-info.ts","../src/auth/token-persistence.ts","../src/auth/token-store.ts","../src/config.ts","../src/client/zendesk-api.ts","../src/routing/registry.ts","../src/utils/article-sections.ts","../src/utils/formatting.ts","../src/utils/pagination.ts","../src/tools/help-center.ts","../src/tools/search.ts","../src/tools/tickets.ts","../src/tools/users.ts","../src/tools/index.ts","../src/server.ts","../src/transports/http.ts","../src/transports/stdio.ts","../src/index.ts"],"sourcesContent":["/**\n * API token authentication for stdio transport.\n * Uses Basic auth: base64(email/token:api_token)\n */\nexport const buildBasicAuthHeader = (email: string, apiToken: string): string => {\n const credentials = `${email}/token:${apiToken}`;\n return `Basic ${Buffer.from(credentials).toString('base64')}`;\n};\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { LogLevel } from '../config';\n\ntype Fields = Record<string, unknown>;\n\n/**\n * Minimal structured logger with two sinks:\n * - stderr (always, gated by the configured level) — the universal floor that\n * every mainstream MCP client captures to a log file;\n * - MCP `notifications/message` (when a server is attached and advertises the\n * `logging` capability) — surfaced by clients that support it, ignored\n * gracefully by those that don't.\n *\n * Both sinks share a single level gate (`LOG_LEVEL`); MCP clients may further\n * raise the floor via `logging/setLevel`, which the SDK filters on its own.\n */\nexport interface Logger {\n debug(event: string, fields?: Fields): void;\n info(event: string, fields?: Fields): void;\n warn(event: string, fields?: Fields): void;\n error(event: string, fields?: Fields): void;\n /** Attach an McpServer so logs are also emitted as MCP notifications. */\n attachServer(server: Pick<McpServer, 'sendLoggingMessage'>): void;\n}\n\nconst SEVERITY: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 };\n\n// Our 4 levels mapped onto the MCP logging levels (note: `warning`, not `warn`).\nconst MCP_LEVEL: Record<LogLevel, 'debug' | 'info' | 'warning' | 'error'> = {\n debug: 'debug',\n info: 'info',\n warn: 'warning',\n error: 'error',\n};\n\n// Field keys whose values must never reach a sink, matched after normalising\n// (lowercase, separators stripped) so `access_token`, `accessToken` and\n// `ACCESS-TOKEN` all collapse to the same key. Diagnostic fields such as\n// `errorCode`, `status` or `error` (a message) are intentionally NOT listed.\nconst REDACTED_KEYS = new Set([\n 'token',\n 'accesstoken',\n 'refreshtoken',\n 'code',\n 'codeverifier',\n 'authorization',\n 'password',\n 'secret',\n 'apitoken',\n 'bearer',\n]);\n\nconst isSensitive = (key: string): boolean =>\n REDACTED_KEYS.has(key.toLowerCase().replace(/[_-]/g, ''));\n\n// Recursively redact: a sensitive *key* anywhere in the tree (top-level or\n// nested in objects/arrays) has its value replaced, so `{ oauth: { token } }`\n// can't leak. Primitives are returned as-is.\nconst redactValue = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(redactValue);\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n out[key] = isSensitive(key) ? '[REDACTED]' : redactValue(val);\n }\n return out;\n }\n return value;\n};\n\nconst renderValue = (value: unknown): string => {\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean' || value === null) {\n return String(value);\n }\n try {\n return JSON.stringify(value);\n } catch {\n return '[unserializable]';\n }\n};\n\nconst formatLine = (level: LogLevel, event: string, fields: Fields): string => {\n const parts = Object.entries(fields).map(([key, value]) => `${key}=${renderValue(value)}`);\n return `[zendesk-mcp] [${level}] ${event}${parts.length ? ` ${parts.join(' ')}` : ''}`;\n};\n\nconst noop = (): void => {};\n\nexport const silentLogger: Logger = {\n debug: noop,\n info: noop,\n warn: noop,\n error: noop,\n attachServer: noop,\n};\n\nexport const createLogger = (level: LogLevel): Logger => {\n const min = SEVERITY[level];\n let server: Pick<McpServer, 'sendLoggingMessage'> | undefined;\n\n const emit = (lvl: LogLevel, event: string, fields?: Fields): void => {\n if (SEVERITY[lvl] < min) return;\n\n const safe = fields ? (redactValue(fields) as Fields) : {};\n console.error(formatLine(lvl, event, safe));\n\n if (server) {\n try {\n void server\n .sendLoggingMessage({\n level: MCP_LEVEL[lvl],\n logger: 'zendesk-mcp-server',\n // `event` last so a caller-supplied `fields.event` can't shadow the\n // canonical event name.\n data: { ...safe, event },\n })\n .catch(noop);\n } catch {\n // Forwarding is best-effort (e.g. transport not connected yet);\n // it must never break the flow that emitted the log.\n }\n }\n };\n\n return {\n debug: (event, fields) => emit('debug', event, fields),\n info: (event, fields) => emit('info', event, fields),\n warn: (event, fields) => emit('warn', event, fields),\n error: (event, fields) => emit('error', event, fields),\n attachServer: (s) => {\n server = s;\n },\n };\n};\n","export const CHARACTER_LIMIT = 25_000;\nexport const DEFAULT_PAGE_SIZE = 100;\nexport const MAX_PAGE_SIZE = 100;\nexport const TOKEN_CACHE_TTL_MS = 5 * 60 * 1000;\n\n// Local port the OAuth PKCE flow listens on for the browser callback. Must match\n// the redirect URL registered in the Zendesk OAuth client. Deliberately picked\n// outside the usual dev range (3000/5000/8080…) and below the OS ephemeral\n// ranges (Linux ≥ 32768, Windows ≥ 49152) so it is neither commonly taken nor\n// grabbed by a transient socket. Override via ZENDESK_OAUTH_CALLBACK_PORT /\n// --callback-port (and register the matching redirect URL in Zendesk).\nexport const DEFAULT_CALLBACK_PORT = 27439;\n\n// Per-attachment cap for inline image content. Images larger than this are\n// returned as text references instead of base64 image content blocks.\n// Aligned with the Anthropic vision API per-image limit.\nexport const MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;\n\n// Maximum number of images embedded as base64 in a single tool call.\n// Remaining images are returned as text references.\nexport const MAX_EMBEDDED_IMAGE_COUNT = 10;\n\n// Hard cap on comment pages fetched when collecting ticket attachments.\n// Overridable via ZENDESK_MAX_COMMENT_PAGES for tickets with many comments.\nexport const MAX_COMMENT_PAGES = Number(process.env['ZENDESK_MAX_COMMENT_PAGES'] ?? 10);\n\n// Thresholds used to nudge callers toward section-scoped article tools\n// (get_article_outline / get_article_section / update_article_section)\n// instead of fetching/rewriting the full body.\nexport const LARGE_ARTICLE_BODY_CHARS = 3_000;\nexport const LARGE_ARTICLE_SECTION_COUNT = 4;\n\nexport const getBaseUrl = (subdomain: string): string => `https://${subdomain}.zendesk.com/api/v2`;\n\nexport const getHelpCenterBaseUrl = (subdomain: string): string =>\n `https://${subdomain}.zendesk.com/api/v2/help_center`;\n\nexport const getOAuthUrls = (subdomain: string) => ({\n authorizeUrl: `https://${subdomain}.zendesk.com/oauth/authorizations/new`,\n tokenUrl: `https://${subdomain}.zendesk.com/oauth/tokens`,\n});\n","import { createHash, randomBytes } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nimport { createServer, type Server } from 'node:http';\nimport { release } from 'node:os';\nimport open from 'open';\nimport { DEFAULT_CALLBACK_PORT, getOAuthUrls } from '../constants';\nimport { type Logger, silentLogger } from '../utils/logger';\n\nconst AUTH_TIMEOUT_MS = 5 * 60 * 1000;\n\n/** Best-effort WSL detection: WSL kernels carry \"microsoft\" in /proc/version. */\nconst detectWsl = (): boolean => {\n if (process.platform !== 'linux') return false;\n try {\n return readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft');\n } catch {\n return false;\n }\n};\n\ninterface BrowserOAuthConfig {\n subdomain: string;\n oauthClientId: string;\n callbackPort?: number | undefined;\n}\n\ninterface TokenResult {\n access_token: string;\n refresh_token?: string;\n token_type: string;\n scope: string;\n // Present only when the Zendesk OAuth client has token expiration enabled.\n // Seconds until the access/refresh token expires.\n expires_in?: number;\n refresh_token_expires_in?: number;\n}\n\n/**\n * Build an actionable error for a callback port that's already taken. The raw\n * Node `EADDRINUSE` is opaque to both the user and the LLM; this spells out the\n * fix (set a free port + register the matching redirect URL in Zendesk). The\n * `(EADDRINUSE)` marker and `code` are kept for diagnostics/tests.\n */\nconst callbackPortInUseError = (port: number, cause: Error): Error =>\n Object.assign(\n new Error(\n `Cannot start the Zendesk OAuth sign-in: local callback port ${port} is already in use ` +\n `by another process. Set ZENDESK_OAUTH_CALLBACK_PORT (or --callback-port) to a free port, ` +\n `then register http://localhost:<port>/callback as a redirect URL in your Zendesk OAuth ` +\n `client. (EADDRINUSE)`,\n ),\n { code: 'EADDRINUSE', cause },\n );\n\n/**\n * Escape a string for safe interpolation into HTML text/attribute context.\n * The local callback server echoes attacker-controllable values (the OAuth\n * `error_description` query param, token-exchange error bodies) back into the\n * browser response; without escaping these are a reflected-XSS sink.\n */\nconst escapeHtml = (value: string): string =>\n value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n\n/**\n * A started OAuth flow: the local callback server is already listening and the\n * browser-open attempt has been made. `authorizeUrl` is available immediately\n * (so callers can surface it to the user without blocking), while `tokenPromise`\n * resolves later, when the user completes the browser flow — or rejects on\n * error/timeout.\n */\nexport interface StartedBrowserAuth {\n authorizeUrl: string;\n tokenPromise: Promise<TokenResult>;\n}\n\nconst generateCodeVerifier = (): string => randomBytes(32).toString('base64url');\n\nconst generateCodeChallenge = (verifier: string): string =>\n createHash('sha256').update(verifier).digest('base64url');\n\n/**\n * Begin the OAuth 2.1 PKCE flow: start the local callback server, attempt to\n * open the browser, and return as soon as the server is listening with the\n * authorize URL plus a promise that settles when the callback arrives.\n *\n * Splitting \"start\" from \"await the token\" lets callers stay non-blocking: they\n * can hand the URL back to the user immediately instead of holding a request\n * open for up to the 5-minute timeout.\n */\nexport const startBrowserAuth = (\n config: BrowserOAuthConfig,\n logger: Logger = silentLogger,\n): Promise<StartedBrowserAuth> => {\n const { subdomain, oauthClientId } = config;\n const { authorizeUrl: authorizeBase, tokenUrl } = getOAuthUrls(subdomain);\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n\n return new Promise<StartedBrowserAuth>((resolveStarted, rejectStarted) => {\n let resolveToken!: (token: TokenResult) => void;\n let rejectToken!: (err: unknown) => void;\n const tokenPromise = new Promise<TokenResult>((resolve, reject) => {\n resolveToken = resolve;\n rejectToken = reject;\n });\n\n let authTimeout: ReturnType<typeof setTimeout> | undefined;\n let callbackServer: Server;\n\n callbackServer = createServer(async (req, res) => {\n const url = new URL(req.url ?? '/', `http://localhost`);\n\n if (url.pathname !== '/callback') {\n res.writeHead(404);\n res.end('Not found');\n return;\n }\n\n const code = url.searchParams.get('code');\n const error = url.searchParams.get('error');\n\n logger.debug('oauth_callback_received', {\n hasCode: Boolean(code),\n hasError: Boolean(error),\n });\n\n if (error) {\n const desc = url.searchParams.get('error_description') ?? error;\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(\n `<html><body><h1>Authentication failed</h1><p>${escapeHtml(desc)}</p></body></html>`,\n );\n clearTimeout(authTimeout);\n callbackServer.close();\n rejectToken(new Error(`OAuth error: ${desc}`));\n return;\n }\n\n if (!code) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end('<html><body><h1>Missing authorization code</h1></body></html>');\n clearTimeout(authTimeout);\n callbackServer.close();\n rejectToken(new Error('Missing authorization code in callback'));\n return;\n }\n\n // Exchange code for token\n try {\n const callbackPort = (callbackServer.address() as { port: number }).port;\n const tokenBody = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n client_id: oauthClientId,\n redirect_uri: `http://localhost:${callbackPort}/callback`,\n code_verifier: codeVerifier,\n });\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: tokenBody.toString(),\n });\n\n logger.debug('oauth_token_exchange', { status: tokenResponse.status });\n\n if (!tokenResponse.ok) {\n const errorBody = await tokenResponse.text();\n throw new Error(`Token exchange failed (${tokenResponse.status}): ${errorBody}`);\n }\n\n const tokenData = (await tokenResponse.json()) as TokenResult;\n logger.info('oauth_authenticated');\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(\n '<html><body>' +\n '<h1>Authentication successful!</h1>' +\n '<p>You can close this tab and return to Claude Code.</p>' +\n '<p>This tab will auto-close in <span id=\"t\">10</span>s.</p>' +\n '<script>' +\n 'let n=10;' +\n 'const el=document.getElementById(\"t\");' +\n 'const i=setInterval(()=>{n--;el.textContent=n;if(n<=0){clearInterval(i);window.close();}},1000);' +\n '</script>' +\n '</body></html>',\n );\n\n clearTimeout(authTimeout);\n callbackServer.close();\n resolveToken(tokenData);\n } catch (err) {\n res.writeHead(500, { 'Content-Type': 'text/html' });\n res.end(\n `<html><body><h1>Token exchange failed</h1><p>${escapeHtml(err instanceof Error ? err.message : String(err))}</p></body></html>`,\n );\n clearTimeout(authTimeout);\n callbackServer.close();\n rejectToken(err);\n }\n });\n\n const requestedPort = config.callbackPort ?? DEFAULT_CALLBACK_PORT;\n\n // Listen failure (e.g. port already in use) before we ever get a URL: the\n // whole start fails so the caller can surface/retry. EADDRINUSE is rewrapped\n // into an actionable message (user *and* LLM can act on it).\n const onStartError = (err: Error) => {\n clearTimeout(authTimeout);\n const code = (err as NodeJS.ErrnoException).code;\n logger.error('oauth_callback_listen_failed', { port: requestedPort, errorCode: code });\n rejectStarted(code === 'EADDRINUSE' ? callbackPortInUseError(requestedPort, err) : err);\n };\n callbackServer.once('error', onStartError);\n\n // Start on fixed port (must match redirect_uri registered in Zendesk OAuth client)\n callbackServer.listen(requestedPort, () => {\n // Now that we're listening, a later server error must settle the *token*\n // flow (the started promise is already resolved) and tear the server down,\n // so the token store doesn't wedge waiting on a promise that never settles.\n callbackServer.off('error', onStartError);\n callbackServer.once('error', (err) => {\n clearTimeout(authTimeout);\n callbackServer.close();\n rejectToken(err);\n });\n\n const port = (callbackServer.address() as { port: number }).port;\n const redirectUri = `http://localhost:${port}/callback`;\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: oauthClientId,\n redirect_uri: redirectUri,\n scope: 'read write',\n code_challenge: codeChallenge,\n code_challenge_method: 'S256',\n });\n\n const authUrl = `${authorizeBase}?${params.toString()}`;\n logger.debug('oauth_callback_listening', { port, redirectUri });\n logger.info('oauth_browser_opening');\n // Full authorize URL is debug-only: it carries the (public) client_id and\n // PKCE challenge — no secret — but stays out of default-level logs.\n logger.debug('oauth_authorize_url', { url: authUrl });\n // Always-on, ungated user-facing fallback: if the browser can't open, the\n // user must see this URL regardless of LOG_LEVEL — do NOT route it through\n // the (level-gated) logger.\n console.error(`Opening browser for Zendesk authentication...`);\n console.error(`If the browser doesn't open, visit: ${authUrl}`);\n\n open(authUrl)\n .then(() => {\n logger.debug('oauth_browser_opened');\n })\n .catch((err: unknown) => {\n // Do NOT swallow: this is the #60 signal. Capture why `open` failed\n // plus environment markers (presence only, never values).\n logger.error('oauth_browser_open_failed', {\n error: err instanceof Error ? err.message : String(err),\n errorCode: (err as NodeJS.ErrnoException | undefined)?.code,\n platform: process.platform,\n release: release(),\n isWsl: detectWsl(),\n hasSystemRoot: Boolean(process.env['SYSTEMROOT']),\n hasWindir: Boolean(process.env['WINDIR']),\n hasComSpec: Boolean(process.env['ComSpec']),\n hasPath: Boolean(process.env['PATH']),\n hasDisplay: Boolean(process.env['DISPLAY']),\n });\n });\n\n // Timeout after 5 minutes. Cleared on every completion path above so a\n // successful auth can't emit a spurious `oauth_timeout` error later.\n authTimeout = setTimeout(() => {\n logger.error('oauth_timeout', { timeoutMs: AUTH_TIMEOUT_MS });\n callbackServer.close();\n rejectToken(new Error('OAuth authentication timed out (5 min). Please try again.'));\n }, AUTH_TIMEOUT_MS);\n authTimeout.unref();\n\n resolveStarted({ authorizeUrl: authUrl, tokenPromise });\n });\n });\n};\n\n/**\n * Convenience wrapper that performs the full PKCE flow and resolves with the\n * token once the browser flow completes (blocking until then). Retained for\n * callers/tests that want the original \"await the token\" semantics.\n */\nexport const authenticateViaBrowser = async (\n config: BrowserOAuthConfig,\n logger: Logger = silentLogger,\n): Promise<TokenResult> => {\n const { tokenPromise } = await startBrowserAuth(config, logger);\n return tokenPromise;\n};\n\n/**\n * Exchange a refresh token for a fresh access token (and a rotated refresh token)\n * without any browser interaction. Public PKCE clients send no `client_secret`.\n * Zendesk refresh tokens are single-use: the caller MUST persist the new\n * `refresh_token` from the response. Throws on a non-2xx (expired/invalid\n * refresh token) so the caller can fall back to the full browser flow.\n */\nexport const refreshAccessToken = async (\n config: { subdomain: string; oauthClientId: string; refreshToken: string },\n logger: Logger = silentLogger,\n): Promise<TokenResult> => {\n const { tokenUrl } = getOAuthUrls(config.subdomain);\n\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n refresh_token: config.refreshToken,\n client_id: config.oauthClientId,\n });\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n logger.debug('oauth_token_refresh', { status: response.status });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Token refresh failed (${response.status}): ${errorBody}`);\n }\n\n const tokenData = (await response.json()) as TokenResult;\n logger.info('oauth_token_refreshed');\n return tokenData;\n};\n","import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport interface PackageInfo {\n name: string;\n version: string;\n}\n\n// Used only if package.json can't be located/parsed (should not happen in a\n// published install). Keeps the server bootable rather than throwing.\nconst FALLBACK: PackageInfo = {\n name: '@fruggr/zendesk-mcp-server',\n version: '0.0.0',\n};\n\n/**\n * Read `name`/`version` from the package's own package.json at runtime instead\n * of hardcoding them. Walks up from this module to the nearest package.json,\n * which resolves correctly both when bundled (`dist/index.js` → repo root) and\n * from source/tests (`src/` has no package.json, so the root is found). Reading\n * at runtime (not inlining at build) matters because semantic-release bumps the\n * version into package.json before publishing, after the build step.\n */\n// package.json can't change during the process lifetime, and createMcpServer\n// (hence this) may run several times (notably across tests) — cache the result.\nlet cached: PackageInfo | undefined;\n\nexport const readPackageInfo = (): PackageInfo => {\n if (cached) return cached;\n // Only readFileSync/JSON.parse can throw here (caught per-iteration below);\n // fileURLToPath/dirname/join don't, so no outer guard is needed.\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let depth = 0; depth < 8; depth++) {\n try {\n // JSON.parse yields `unknown`; validate at runtime rather than asserting.\n const raw: unknown = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));\n if (raw && typeof raw === 'object') {\n const pkg = raw as Partial<PackageInfo>;\n if (typeof pkg.name === 'string' && typeof pkg.version === 'string') {\n cached = { name: pkg.name, version: pkg.version };\n return cached;\n }\n }\n } catch {\n // No readable/valid package.json here — keep walking up.\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n cached = FALLBACK;\n return cached;\n};\n","import { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { type Logger, silentLogger } from '../utils/logger';\nimport { readPackageInfo } from '../utils/package-info';\n\n/**\n * On-disk OAuth token record. `expiresAt` is an absolute epoch-ms timestamp\n * (derived from the OAuth `expires_in`) so expiry survives restarts; `undefined`\n * means a non-expiring token (Zendesk's default until expiration is enabled).\n */\nexport interface PersistedToken {\n accessToken: string;\n refreshToken?: string | undefined;\n expiresAt?: number | undefined;\n}\n\nconst isWindows = process.platform === 'win32';\n\n/**\n * Config-dir segments derived from the *scoped* package name\n * (`@fruggr/zendesk-mcp-server` → `fruggr` + `zendesk-mcp-server`) so the path is\n * vendor-namespaced and can't collide with another `zendesk-mcp-server`.\n */\nconst appDirSegments = (): string[] => {\n const { name } = readPackageInfo();\n const scoped = /^@([^/]+)\\/(.+)$/.exec(name);\n return scoped?.[1] && scoped[2] ? [scoped[1], scoped[2]] : [name];\n};\n\n// OS config dir holding the per-subdomain token files: `%APPDATA%` on Windows,\n// `$XDG_CONFIG_HOME` (falling back to `~/.config`) elsewhere.\nconst configDir = (): string => {\n const segments = appDirSegments();\n if (isWindows) {\n const base = process.env['APPDATA'] ?? join(homedir(), 'AppData', 'Roaming');\n return join(base, ...segments);\n }\n const base = process.env['XDG_CONFIG_HOME'] ?? join(homedir(), '.config');\n return join(base, ...segments);\n};\n\n// Zendesk subdomains are [a-z0-9-]; sanitize defensively so a crafted value\n// can neither escape the config dir nor smuggle a path separator.\nconst safeName = (subdomain: string): string => subdomain.replace(/[^a-z0-9-]/gi, '_');\n\n/**\n * Path to the token file for a subdomain. Each subdomain gets its **own** file\n * (`<subdomain>.json`, a single record) so concurrent processes for different\n * subdomains never read-modify-write a shared file — no merge, no clobber.\n * `ZENDESK_TOKEN_FILE` overrides with an explicit path (a single file; use the\n * default layout for multi-subdomain installs).\n */\nexport const resolveTokenPath = (subdomain: string): string => {\n const override = process.env['ZENDESK_TOKEN_FILE'];\n if (override) return override;\n return join(configDir(), `${safeName(subdomain)}.json`);\n};\n\n// Atomic write (tmp + rename) so a crash mid-write can't leave a truncated file,\n// with owner-only perms on the file and its dir where the OS supports it.\nconst writeFileAtomic = (path: string, record: PersistedToken): void => {\n const dir = dirname(path);\n mkdirSync(dir, { recursive: true });\n const tmp = `${path}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(record, null, 2), 'utf8');\n if (!isWindows) chmodSync(tmp, 0o600);\n renameSync(tmp, path);\n if (!isWindows) {\n try {\n chmodSync(dir, 0o700);\n } catch {\n // Best-effort: tightening the dir is a bonus, not a requirement.\n }\n }\n};\n\n// A missing file or unparseable/garbage content is treated as \"no token yet\"\n// rather than an error: persistence must never wedge the auth flow.\nexport const loadToken = (path: string): PersistedToken | undefined => {\n try {\n const raw: unknown = JSON.parse(readFileSync(path, 'utf8'));\n if (raw && typeof raw === 'object' && typeof (raw as PersistedToken).accessToken === 'string') {\n return raw as PersistedToken;\n }\n } catch {\n // absent or corrupt → no token\n }\n return undefined;\n};\n\nexport const saveToken = (\n path: string,\n record: PersistedToken,\n logger: Logger = silentLogger,\n): void => {\n try {\n writeFileAtomic(path, record);\n logger.debug('token_persisted');\n } catch (err) {\n // Never fail the auth flow because the token couldn't be cached to disk.\n logger.warn('token_persist_failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n};\n\nexport const clearToken = (path: string, logger: Logger = silentLogger): void => {\n try {\n rmSync(path, { force: true });\n logger.debug('token_cleared');\n } catch (err) {\n logger.warn('token_clear_failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n};\n","import { type Logger, silentLogger } from '../utils/logger';\nimport { refreshAccessToken, startBrowserAuth } from './browser-oauth';\nimport {\n clearToken as clearPersistedToken,\n loadToken,\n type PersistedToken,\n resolveTokenPath,\n saveToken,\n} from './token-persistence';\n\ntype StoredToken = PersistedToken;\n\n// Refresh slightly before the real expiry so a request never races a token that\n// dies in flight.\nconst EXPIRY_SKEW_MS = 60_000;\n\n/**\n * Signals that interactive sign-in is required. The message is user-facing: MCP\n * surfaces it as the tool-call error text, so it carries the authorize URL and\n * tells the user to authenticate and retry. Kept as an Error factory (not a\n * class) to match the repo's functional style.\n */\nexport type AuthRequiredError = Error & { readonly authorizeUrl: string };\n\nexport const createAuthRequiredError = (authorizeUrl: string): AuthRequiredError =>\n Object.assign(\n new Error(\n 'Zendesk authentication required. A browser window should have opened for you to sign in. ' +\n 'If it did not, open this URL in your browser, then retry your request:\\n' +\n authorizeUrl,\n ),\n { name: 'AuthRequiredError', authorizeUrl } as const,\n );\n\nexport const isAuthRequiredError = (err: unknown): err is AuthRequiredError =>\n err instanceof Error &&\n err.name === 'AuthRequiredError' &&\n typeof (err as AuthRequiredError).authorizeUrl === 'string';\n\n// Compute the absolute expiry (epoch ms) from an OAuth `expires_in` (seconds),\n// or `undefined` for a non-expiring token.\nconst expiryFrom = (expiresIn: number | undefined): number | undefined =>\n typeof expiresIn === 'number' ? Date.now() + expiresIn * 1000 : undefined;\n\nexport const createTokenStore = (\n config: { subdomain: string; oauthClientId: string; callbackPort?: number | undefined },\n logger: Logger = silentLogger,\n) => {\n const tokenPath = resolveTokenPath(config.subdomain);\n // Seed the in-memory cache from disk so a restart (notably the Cowork-on-Windows\n // process churn) reuses the existing token instead of re-prompting.\n let token: StoredToken | undefined = loadToken(tokenPath);\n if (token) logger.debug('oauth_token_loaded_from_disk');\n\n // The authorize URL of the in-flight flow (set once the callback server is\n // listening); `undefined` means no flow is currently pending.\n let authorizeUrl: string | undefined;\n // Resolves to the authorize URL while a flow is being started; guards against\n // launching multiple browser flows for concurrent first calls.\n let starting: Promise<string> | undefined;\n // De-dupes concurrent refresh attempts so a burst of expired-token calls\n // triggers a single network round-trip.\n let refreshing: Promise<string | undefined> | undefined;\n\n const persist = (t: StoredToken): void => saveToken(tokenPath, t, logger);\n\n const setToken = (accessToken: string, refreshToken?: string | undefined) => {\n token = { accessToken, refreshToken };\n persist(token);\n };\n\n const isExpired = (t: StoredToken): boolean =>\n typeof t.expiresAt === 'number' && Date.now() >= t.expiresAt - EXPIRY_SKEW_MS;\n\n // Try to silently mint a fresh access token from the stored refresh token.\n // Resolves to the new access token, or `undefined` if there's nothing to\n // refresh / the refresh failed (in which case the dead token is dropped).\n const tryRefresh = async (current: StoredToken): Promise<string | undefined> => {\n if (!current.refreshToken) return undefined;\n try {\n const result = await refreshAccessToken(\n {\n subdomain: config.subdomain,\n oauthClientId: config.oauthClientId,\n refreshToken: current.refreshToken,\n },\n logger,\n );\n // Zendesk rotates the refresh token on every use — persist the new one (or\n // keep the current one if the response omitted it).\n token = {\n accessToken: result.access_token,\n refreshToken: result.refresh_token ?? current.refreshToken,\n expiresAt: expiryFrom(result.expires_in),\n };\n persist(token);\n logger.info('oauth_token_refreshed_cached');\n return token.accessToken;\n } catch (err) {\n logger.warn('oauth_token_refresh_failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n // Refresh token expired/invalid → drop it so we fall back to a fresh flow.\n token = undefined;\n clearPersistedToken(tokenPath, logger);\n return undefined;\n }\n };\n\n const beginAuth = (): Promise<string> => {\n logger.info('oauth_auth_start');\n return startBrowserAuth(\n {\n subdomain: config.subdomain,\n oauthClientId: config.oauthClientId,\n callbackPort: config.callbackPort,\n },\n logger,\n )\n .then((started) => {\n authorizeUrl = started.authorizeUrl;\n started.tokenPromise\n .then((result) => {\n token = {\n accessToken: result.access_token,\n refreshToken: result.refresh_token,\n expiresAt: expiryFrom(result.expires_in),\n };\n persist(token);\n logger.info('oauth_token_cached');\n })\n .catch((err) => {\n logger.warn('oauth_auth_failed', {\n error: err instanceof Error ? err.message : String(err),\n });\n })\n .finally(() => {\n // Let the next call start a fresh flow: on success the cached token\n // short-circuits anyway; on failure/timeout we want a clean retry.\n starting = undefined;\n authorizeUrl = undefined;\n });\n return started.authorizeUrl;\n })\n .catch((err) => {\n // The callback server couldn't even start listening (e.g. port in use).\n // Reset so a later call can retry, and surface the underlying error.\n starting = undefined;\n throw err;\n });\n };\n\n const getToken = async (): Promise<string> => {\n if (token && !isExpired(token)) {\n logger.debug('oauth_token_cache_hit');\n return token.accessToken;\n }\n\n // Expired (or near-expiry) but refreshable → refresh silently before falling\n // back to a browser prompt. Concurrent callers share the one attempt.\n if (token?.refreshToken) {\n if (!refreshing) {\n refreshing = tryRefresh(token).finally(() => {\n refreshing = undefined;\n });\n }\n const refreshed = await refreshing;\n if (refreshed) return refreshed;\n }\n\n if (!starting) {\n starting = beginAuth();\n }\n\n // Fail fast: don't hold the tool call open for the whole browser flow.\n // Surface the authorize URL so the user can sign in, then retry — the\n // callback server keeps running in the background and caches the token,\n // so the next call succeeds.\n const url = authorizeUrl ?? (await starting);\n throw createAuthRequiredError(url);\n };\n\n // Backstop for an access token the server rejected mid-life (e.g. revoked).\n // A 401 invalidates the *access* token, not necessarily the refresh token —\n // so keep the latter and just mark the access token expired, letting the next\n // getToken attempt a silent refresh before falling back to the browser. Only\n // when there's no refresh token do we wipe the record entirely.\n const invalidate = (): void => {\n if (token?.refreshToken) {\n token = { accessToken: token.accessToken, refreshToken: token.refreshToken, expiresAt: 0 };\n persist(token);\n } else {\n token = undefined;\n clearPersistedToken(tokenPath, logger);\n }\n logger.info('oauth_token_invalidated');\n };\n\n return { getToken, setToken, invalidate };\n};\n","import * as z from 'zod/v4';\n\nexport const ToolMode = z.enum(['single', 'namespace', 'all']);\nexport type ToolMode = z.infer<typeof ToolMode>;\n\nexport const LogLevel = z.enum(['debug', 'info', 'warn', 'error']);\nexport type LogLevel = z.infer<typeof LogLevel>;\n\nexport const Namespace = z.enum(['tickets', 'help_center', 'users']);\nexport type Namespace = z.infer<typeof Namespace>;\n\nexport const Transport = z.enum(['stdio', 'http']);\nexport type Transport = z.infer<typeof Transport>;\n\nexport const ConfigSchema = z.object({\n subdomain: z.string().min(1, 'ZENDESK_SUBDOMAIN is required'),\n oauthClientId: z.string().min(1),\n zendeskEmail: z.string().optional(),\n zendeskApiToken: z.string().optional(),\n logLevel: LogLevel,\n mode: ToolMode,\n readOnly: z.boolean(),\n namespaces: z.array(Namespace).optional(),\n tools: z.array(z.string()).optional(),\n transport: Transport,\n host: z.string().min(1),\n port: z.number().int().min(0).max(65535),\n publicUrl: z.string().url().optional(),\n /**\n * Additional browser origins allowed by CORS in HTTP mode. The default\n * allowlist (the major web MCP clients + localhost-any-port for dev) is\n * always applied; this list extends it. Native MCP clients (Claude\n * Desktop, Claude Code CLI, Cursor, VS Code, Zed…) are unaffected\n * because they send no Origin header.\n */\n corsOrigins: z\n .array(\n z\n .string()\n .url()\n // Browsers send `Origin` with no trailing slash or path, and the CORS\n // allowlist matches by strict equality. Normalize the configured value\n // to its origin so `https://app.example.com/` (natural when\n // copy-pasting a URL) still matches at runtime.\n .transform((value) => new URL(value).origin)\n .refine((origin) => origin !== 'null', {\n message: 'CORS origin must be an http(s) URL with a host',\n }),\n )\n .default([]),\n callbackPort: z.number().int().min(1).max(65535).optional(),\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\ninterface CliResult {\n subdomain?: string;\n mode?: string;\n readOnly?: boolean;\n namespaces?: string[];\n tools?: string[];\n logLevel?: string;\n transport?: string;\n host?: string;\n port?: number;\n publicUrl?: string;\n corsOrigins?: string[];\n callbackPort?: number;\n}\n\n// Number.parseInt('8080abc', 10) === 8080 — silently accepts a numeric\n// prefix. Validate strictly so malformed values fail loudly instead. Range\n// checks (0 vs 1 minimum etc.) stay in ConfigSchema, the single authority.\n//\n// The error intentionally does NOT echo the offending value: when `label`\n// names a variable CodeQL's heuristics treat as sensitive (anything with\n// \"OAUTH\" / \"TOKEN\" / etc. in the name, like ZENDESK_OAUTH_CALLBACK_PORT),\n// reflecting `raw` into a thrown Error.message that bubbles up to the\n// `console.error('Fatal error:', error)` in src/index.ts gets flagged as\n// `js/clear-text-logging`. The label alone tells the operator which knob is\n// wrong; they can re-read their env / CLI to see what they actually set.\nconst parsePort = (raw: string, label: string): number => {\n if (!/^\\d+$/.test(raw)) {\n throw new Error(`Invalid ${label} value. Expected an integer 0-65535.`);\n }\n return Number(raw);\n};\n\nconst parsePortEnv = (raw: string | undefined, label: string): number | undefined =>\n raw === undefined || raw === '' ? undefined : parsePort(raw, label);\n\nconst parseCliArgs = (args: string[]): CliResult => {\n const result: CliResult = {};\n let positionalIndex = 0;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === undefined) continue;\n const next = args[i + 1];\n\n if (arg === '--mode' && next) {\n result.mode = next;\n i++;\n } else if (arg === '--read-only') {\n result.readOnly = true;\n } else if (arg === '--namespace' && next) {\n result.namespaces = result.namespaces ?? [];\n result.namespaces.push(next);\n i++;\n } else if (arg === '--tool' && next) {\n result.tools = result.tools ?? [];\n result.tools.push(next);\n i++;\n } else if (arg === '--log-level' && next) {\n result.logLevel = next;\n i++;\n } else if (arg === '--transport' && next) {\n result.transport = next;\n i++;\n } else if (arg === '--host' && next) {\n result.host = next;\n i++;\n } else if (arg === '--port' && next) {\n result.port = parsePort(next, '--port');\n i++;\n } else if (arg === '--public-url' && next) {\n result.publicUrl = next;\n i++;\n } else if (arg === '--cors-origin' && next) {\n result.corsOrigins = result.corsOrigins ?? [];\n result.corsOrigins.push(next);\n i++;\n } else if (arg === '--callback-port' && next) {\n result.callbackPort = parsePort(next, '--callback-port');\n i++;\n } else if (!arg.startsWith('-') && positionalIndex === 0) {\n result.subdomain = arg;\n positionalIndex++;\n }\n }\n\n return result;\n};\n\nexport const loadConfig = (argv: string[] = process.argv.slice(2)): Config => {\n const cli = parseCliArgs(argv);\n\n const subdomain = cli.subdomain ?? process.env['ZENDESK_SUBDOMAIN'] ?? '';\n const oauthClientId =\n process.env['ZENDESK_OAUTH_CLIENT_ID'] ?? (subdomain ? `${subdomain}_zendesk` : '');\n\n const mode = cli.tools?.length ? 'all' : (cli.mode ?? 'namespace');\n\n const transport = cli.transport ?? process.env['TRANSPORT'] ?? 'stdio';\n const host = cli.host ?? process.env['HOST'] ?? '0.0.0.0';\n const port = cli.port ?? parsePortEnv(process.env['PORT'], 'PORT') ?? 3000;\n const publicUrl = cli.publicUrl ?? process.env['PUBLIC_URL'];\n\n // CORS allowlist extension: CLI flags first, then comma-separated env var.\n // The defaults (major web MCP clients + localhost-any-port) are baked into\n // the HTTP transport — this list ADDS to them, never replaces them.\n const corsFromEnv = (process.env['CORS_ORIGIN'] ?? '')\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n const corsOrigins = [...(cli.corsOrigins ?? []), ...corsFromEnv];\n\n const callbackPort =\n cli.callbackPort ??\n parsePortEnv(process.env['ZENDESK_OAUTH_CALLBACK_PORT'], 'ZENDESK_OAUTH_CALLBACK_PORT');\n\n const zendeskEmail = process.env['ZENDESK_EMAIL'];\n const zendeskApiToken = process.env['ZENDESK_API_TOKEN'];\n\n // API token auth in HTTP mode would expose the issuing user's full rights to\n // anyone reaching the server (shared static credential). Refuse only when\n // BOTH are set — a stray ZENDESK_EMAIL in the shell environment is harmless\n // by itself, and rejecting it would surprise operators who intended OAuth.\n if (transport === 'http' && zendeskEmail && zendeskApiToken) {\n throw new Error(\n 'API token authentication (ZENDESK_EMAIL + ZENDESK_API_TOKEN) is not supported in HTTP mode. ' +\n 'HTTP mode requires per-user OAuth 2.1 PKCE - unset these variables and configure your ' +\n 'MCP client to perform the OAuth flow against Zendesk.',\n );\n }\n\n return ConfigSchema.parse({\n subdomain,\n oauthClientId,\n zendeskEmail,\n zendeskApiToken,\n logLevel: cli.logLevel ?? process.env['LOG_LEVEL'] ?? 'info',\n mode,\n readOnly: cli.readOnly ?? false,\n namespaces: cli.namespaces,\n tools: cli.tools,\n transport,\n host,\n port,\n publicUrl,\n corsOrigins,\n callbackPort,\n });\n};\n","import { getBaseUrl, getHelpCenterBaseUrl } from '../constants.js';\n\nexport class ZendeskApiError extends Error {\n constructor(\n public readonly status: number,\n public readonly statusText: string,\n public readonly body: string,\n ) {\n super(ZendeskApiError.buildMessage(status, statusText, body));\n this.name = 'ZendeskApiError';\n }\n\n private static buildMessage(status: number, statusText: string, body: string): string {\n switch (status) {\n case 401:\n return 'Authentication failed. Your Zendesk token may be expired or invalid. Re-authenticate to get a new token.';\n case 403:\n return 'Permission denied. Your Zendesk account does not have access to this resource.';\n case 404:\n return `Resource not found. Please verify the ID is correct. (${statusText})`;\n case 422:\n return `Validation error: ${body}`;\n case 429:\n return 'Rate limit exceeded. Please wait before making more requests.';\n default:\n return `Zendesk API error ${status}: ${statusText}. ${body}`;\n }\n }\n}\n\nexport interface ZendeskRequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE';\n body?: unknown;\n params?: Record<string, string>;\n}\n\n// token is either a Bearer OAuth token or a \"Basic xxx\" string (stdio API token mode)\nconst buildAuthHeader = (token: string): string =>\n token.startsWith('Basic ') ? token : `Bearer ${token}`;\n\nconst buildUrl = (base: string, path: string, params?: Record<string, string>): string => {\n const url = new URL(`${base}${path}`);\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value);\n }\n }\n return url.toString();\n};\n\nconst executeRequest = async <T>(\n url: string,\n token: string,\n options: ZendeskRequestOptions = {},\n): Promise<T> => {\n const { method = 'GET', body } = options;\n\n const headers: Record<string, string> = {\n Authorization: buildAuthHeader(token),\n Accept: 'application/json',\n };\n\n if (body) {\n headers['Content-Type'] = 'application/json';\n }\n\n const init: RequestInit = { method, headers };\n if (body) {\n init.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, init);\n\n if (!response.ok) {\n const responseBody = await response.text();\n throw new ZendeskApiError(response.status, response.statusText, responseBody);\n }\n\n if (response.status === 204) {\n return {} as T;\n }\n\n return response.json() as Promise<T>;\n};\n\nexport const zendeskGet = <T>(\n subdomain: string,\n token: string,\n path: string,\n params?: Record<string, string>,\n): Promise<T> => {\n const url = buildUrl(getBaseUrl(subdomain), path, params);\n return executeRequest<T>(url, token);\n};\n\nexport const zendeskPost = <T>(\n subdomain: string,\n token: string,\n path: string,\n body: unknown,\n): Promise<T> => {\n const url = buildUrl(getBaseUrl(subdomain), path);\n return executeRequest<T>(url, token, { method: 'POST', body });\n};\n\nexport const zendeskPut = <T>(\n subdomain: string,\n token: string,\n path: string,\n body: unknown,\n): Promise<T> => {\n const url = buildUrl(getBaseUrl(subdomain), path);\n return executeRequest<T>(url, token, { method: 'PUT', body });\n};\n\nexport const helpCenterGet = <T>(\n subdomain: string,\n token: string,\n path: string,\n params?: Record<string, string>,\n): Promise<T> => {\n const url = buildUrl(getHelpCenterBaseUrl(subdomain), path, params);\n return executeRequest<T>(url, token);\n};\n\nexport const helpCenterPost = <T>(\n subdomain: string,\n token: string,\n path: string,\n body: unknown,\n): Promise<T> => {\n const url = buildUrl(getHelpCenterBaseUrl(subdomain), path);\n return executeRequest<T>(url, token, { method: 'POST', body });\n};\n\nexport const helpCenterPut = <T>(\n subdomain: string,\n token: string,\n path: string,\n body: unknown,\n): Promise<T> => {\n const url = buildUrl(getHelpCenterBaseUrl(subdomain), path);\n return executeRequest<T>(url, token, { method: 'PUT', body });\n};\n\nexport const fetchZendeskBinary = async (\n subdomain: string,\n token: string,\n contentUrl: string,\n): Promise<{ data: Buffer; contentType: string }> => {\n const expectedHost = `${subdomain}.zendesk.com`;\n const headers: Record<string, string> = {};\n if (new URL(contentUrl).hostname === expectedHost) {\n headers['Authorization'] = buildAuthHeader(token);\n }\n const response = await fetch(contentUrl, { headers });\n if (!response.ok) {\n const body = await response.text();\n throw new ZendeskApiError(response.status, response.statusText, body);\n }\n const contentType = response.headers.get('content-type') ?? 'application/octet-stream';\n const arrayBuffer = await response.arrayBuffer();\n return { data: Buffer.from(arrayBuffer), contentType };\n};\n\nexport const helpCenterUpload = async <T>(\n subdomain: string,\n token: string,\n path: string,\n formData: FormData,\n): Promise<T> => {\n const url = buildUrl(getHelpCenterBaseUrl(subdomain), path);\n const response = await fetch(url, {\n method: 'POST',\n headers: { Authorization: buildAuthHeader(token) },\n body: formData,\n });\n\n if (!response.ok) {\n const responseBody = await response.text();\n throw new ZendeskApiError(response.status, response.statusText, responseBody);\n }\n\n return response.json() as Promise<T>;\n};\n","import type { Namespace } from '../config';\nimport type { ToolDefinition } from '../tools/definitions';\n\nexport interface FilterOptions {\n readOnly: boolean;\n namespaces?: Namespace[] | undefined;\n tools?: string[] | undefined;\n}\n\nexport const filterTools = (allTools: ToolDefinition[], options: FilterOptions): ToolDefinition[] =>\n allTools.filter((tool) => {\n if (options.readOnly && !tool.readOnly) return false;\n if (options.namespaces?.length && !options.namespaces.includes(tool.namespace)) return false;\n if (options.tools?.length && !options.tools.includes(tool.name)) return false;\n return true;\n });\n\nexport const groupByNamespace = (tools: ToolDefinition[]): Map<string, ToolDefinition[]> => {\n const grouped = new Map<string, ToolDefinition[]>();\n for (const tool of tools) {\n const existing = grouped.get(tool.namespace) ?? [];\n existing.push(tool);\n grouped.set(tool.namespace, existing);\n }\n return grouped;\n};\n","import * as cheerio from 'cheerio';\nimport type { Element } from 'hast';\nimport { toHtml } from 'hast-util-to-html';\nimport type { Handle } from 'hast-util-to-mdast';\nimport rehypeParse from 'rehype-parse';\nimport rehypeRaw from 'rehype-raw';\nimport rehypeRemark from 'rehype-remark';\nimport rehypeStringify from 'rehype-stringify';\nimport remarkGfm from 'remark-gfm';\nimport remarkParse from 'remark-parse';\nimport remarkRehype from 'remark-rehype';\nimport remarkStringify from 'remark-stringify';\nimport { unified } from 'unified';\n\nexport interface Section {\n index: number;\n heading: string;\n headingTag: string;\n level: number;\n html: string;\n wordCount: number;\n}\n\nconst HEADING_LEVELS = new Set(['h1', 'h2', 'h3']);\n\nconst countWords = (text: string): number => {\n const trimmed = text.trim();\n if (!trimmed) return 0;\n return trimmed.split(/\\s+/).length;\n};\n\nconst textOf = (html: string): string => {\n if (!html) return '';\n const $ = cheerio.load(`<div>${html}</div>`, null, false);\n return $('div').first().text();\n};\n\nexport const parseSections = (html: string): Section[] => {\n if (!html?.trim()) return [];\n\n const $ = cheerio.load(html, null, false);\n const children = $.root().contents().toArray();\n\n const introParts: string[] = [];\n const sections: Array<{\n heading: string;\n headingTag: string;\n level: number;\n contentParts: string[];\n }> = [];\n let current: (typeof sections)[number] | null = null;\n\n for (const node of children) {\n const tagName = node.type === 'tag' ? node.name.toLowerCase() : '';\n\n if (HEADING_LEVELS.has(tagName)) {\n const level = Number.parseInt(tagName.slice(1), 10);\n current = {\n heading: $(node).text().trim(),\n headingTag: tagName,\n level,\n contentParts: [],\n };\n sections.push(current);\n continue;\n }\n\n const outer = $.html(node);\n if (current) {\n current.contentParts.push(outer);\n } else {\n introParts.push(outer);\n }\n }\n\n const result: Section[] = [];\n\n if (introParts.length > 0) {\n const introHtml = introParts.join('');\n result.push({\n index: 0,\n heading: 'intro',\n headingTag: '',\n level: 0,\n html: introHtml,\n wordCount: countWords(textOf(introHtml)),\n });\n }\n\n for (const s of sections) {\n const sectionHtml = s.contentParts.join('');\n result.push({\n index: result.length,\n heading: s.heading,\n headingTag: s.headingTag,\n level: s.level,\n html: sectionHtml,\n wordCount: countWords(textOf(sectionHtml)),\n });\n }\n\n return result;\n};\n\nexport const replaceSectionContent = (\n html: string,\n sectionIndex: number,\n newHtml: string,\n): string => {\n const sections = parseSections(html);\n if (sectionIndex < 0 || sectionIndex >= sections.length) {\n throw new Error(\n `Section index ${sectionIndex} out of range (valid: 0-${Math.max(0, sections.length - 1)})`,\n );\n }\n\n return sections\n .map((section, idx) => {\n const content = idx === sectionIndex ? newHtml : section.html;\n if (section.level === 0) return content;\n return `<${section.headingTag}>${section.heading}</${section.headingTag}>${content}`;\n })\n .join('');\n};\n\n// Keep structural HTML that markdown flattens lossily: <pre> with inline <br>\n// collapses to a single line, and <table> cells with multiple <p> break GFM\n// pipe tables. Leaving them as raw HTML is safer for round-trip.\nconst keepAsHtml: Handle = (_state, node) => ({\n type: 'html',\n value: toHtml(node as Element),\n});\n\nconst htmlToMdProcessor = unified()\n .use(rehypeParse, { fragment: true })\n .use(rehypeRemark, { handlers: { table: keepAsHtml, pre: keepAsHtml } })\n .use(remarkGfm)\n .use(remarkStringify, { bullet: '-', emphasis: '_', fences: true });\n\nconst mdToHtmlProcessor = unified()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkRehype, { allowDangerousHtml: true })\n .use(rehypeRaw)\n .use(rehypeStringify);\n\nexport const htmlToMarkdown = (html: string): string => {\n if (!html) return '';\n return String(htmlToMdProcessor.processSync(html));\n};\n\nexport const markdownToHtml = (markdown: string): string => {\n if (!markdown) return '';\n return String(mdToHtmlProcessor.processSync(markdown));\n};\n","import { CHARACTER_LIMIT } from '../constants.js';\nimport type {\n PaginationMeta,\n ZendeskArticle,\n ZendeskArticleAttachment,\n ZendeskCategory,\n ZendeskComment,\n ZendeskContentTag,\n ZendeskLabel,\n ZendeskOrganization,\n ZendeskPermissionGroup,\n ZendeskSection,\n ZendeskTicket,\n ZendeskTranslation,\n ZendeskUser,\n ZendeskUserSegment,\n} from '../types.js';\n\nexport const truncateIfNeeded = (text: string): string => {\n if (text.length <= CHARACTER_LIMIT) return text;\n return `${text.slice(0, CHARACTER_LIMIT)}\\n\\n--- Response truncated (${text.length} chars, limit ${CHARACTER_LIMIT}). Use pagination or filters to reduce results. ---`;\n};\n\nconst formatPagination = (meta: PaginationMeta): string => {\n const parts = [`Results: ${meta.count}`];\n if (meta.has_more) {\n parts.push(`More available (cursor: ${meta.after_cursor})`);\n }\n return parts.join(' | ');\n};\n\nexport const formatTicket = (ticket: ZendeskTicket): string =>\n [\n `## Ticket #${ticket.id}: ${ticket.subject}`,\n `- **Status**: ${ticket.status} | **Priority**: ${ticket.priority ?? 'none'} | **Type**: ${ticket.type ?? 'none'}`,\n `- **Requester**: ${ticket.requester_id} | **Assignee**: ${ticket.assignee_id ?? 'unassigned'}`,\n `- **Tags**: ${ticket.tags.length > 0 ? ticket.tags.join(', ') : 'none'}`,\n `- **Created**: ${ticket.created_at} | **Updated**: ${ticket.updated_at}`,\n ticket.description ? `\\n${ticket.description}` : '',\n ]\n .filter(Boolean)\n .join('\\n');\n\nexport const formatComment = (comment: ZendeskComment): string => {\n const lines = [\n `### ${comment.public ? 'Public comment' : 'Internal note'} by ${comment.author_id}`,\n `*${comment.created_at}*`,\n ];\n if (comment.attachments?.length) {\n const summary = comment.attachments.map((a) => `#${a.id} (${a.content_type})`).join(', ');\n lines.push(`Attachments: ${summary}`);\n }\n lines.push('', comment.body);\n return lines.join('\\n');\n};\n\nexport const formatUser = (user: ZendeskUser): string =>\n [\n `## ${user.name} (${user.id})`,\n `- **Email**: ${user.email}`,\n `- **Role**: ${user.role}`,\n user.role_type != null ? `- **Role type**: ${user.role_type}` : '',\n `- **Active**: ${user.active}`,\n user.organization_id ? `- **Organization**: ${user.organization_id}` : '',\n ]\n .filter(Boolean)\n .join('\\n');\n\nexport const formatOrganization = (org: ZendeskOrganization): string =>\n [\n `## ${org.name} (${org.id})`,\n org.details ? `- **Details**: ${org.details}` : '',\n org.domain_names.length > 0 ? `- **Domains**: ${org.domain_names.join(', ')}` : '',\n org.tags.length > 0 ? `- **Tags**: ${org.tags.join(', ')}` : '',\n ]\n .filter(Boolean)\n .join('\\n');\n\nexport const formatArticleSummary = (article: ZendeskArticle): string =>\n [\n `## ${article.title} (${article.id})`,\n `- **Locale**: ${article.locale} | **Source locale**: ${article.source_locale}`,\n `- **Section**: ${article.section_id} | **Draft**: ${article.draft}`,\n typeof article.position === 'number' ? `- **Position**: ${article.position}` : '',\n article.label_names.length > 0 ? `- **Labels**: ${article.label_names.join(', ')}` : '',\n `- **Created**: ${article.created_at} | **Updated**: ${article.updated_at}`,\n ]\n .filter(Boolean)\n .join('\\n');\n\nexport const formatArticle = (article: ZendeskArticle): string =>\n [formatArticleSummary(article), '', article.body].join('\\n');\n\nexport const formatTranslationSummary = (translation: ZendeskTranslation): string =>\n [\n `## Translation: ${translation.locale} (${translation.id})`,\n `- **Title**: ${translation.title}`,\n `- **Draft**: ${translation.draft}`,\n `- **Updated**: ${translation.updated_at}`,\n ].join('\\n');\n\nexport const formatTranslation = (translation: ZendeskTranslation): string =>\n [formatTranslationSummary(translation), '', translation.body].join('\\n');\n\nexport const formatCategory = (category: ZendeskCategory): string =>\n `- **${category.name}** (${category.id}) — ${category.description || 'No description'}`;\n\nexport const formatSection = (section: ZendeskSection): string =>\n `- **${section.name}** (${section.id}) — Category: ${section.category_id} — ${section.description || 'No description'}`;\n\nexport const formatPermissionGroup = (group: ZendeskPermissionGroup): string =>\n `- **${group.name}** (${group.id})${group.built_in ? ' — Built-in' : ''}`;\n\nexport const formatContentTag = (tag: ZendeskContentTag): string => `- **${tag.name}** (${tag.id})`;\n\nexport const formatLabel = (label: ZendeskLabel): string => `- **${label.name}** (${label.id})`;\n\nexport const formatUserSegment = (segment: ZendeskUserSegment): string =>\n `- **${segment.name}** (${segment.id}) — ${segment.user_type}${segment.built_in ? ' — Built-in' : ''}`;\n\nexport const formatAttachment = (attachment: ZendeskArticleAttachment): string =>\n `- **${attachment.file_name}** (${attachment.id}) — ${attachment.content_type} — ${attachment.size} bytes`;\n\nexport const formatList = <T>(\n items: T[],\n formatter: (item: T) => string,\n meta?: PaginationMeta,\n): string => {\n const header = meta ? formatPagination(meta) : '';\n const body = items.map(formatter).join('\\n\\n');\n const text = [header, body].filter(Boolean).join('\\n\\n');\n return truncateIfNeeded(text);\n};\n","import type { PaginationMeta, ZendeskListResponse } from '../types';\n\n// Cursor-based pagination (for list endpoints: /tickets, /organizations, etc.)\nexport const buildCursorParams = (pageSize: number, cursor?: string): Record<string, string> => {\n const params: Record<string, string> = {\n 'page[size]': String(pageSize),\n };\n if (cursor) {\n params['page[after]'] = cursor;\n }\n return params;\n};\n\n// Offset-based pagination (for search endpoints: /search, /help_center/articles/search)\nexport const buildOffsetParams = (perPage: number, page?: number): Record<string, string> => {\n const params: Record<string, string> = {\n per_page: String(perPage),\n };\n if (page && page > 1) {\n params['page'] = String(page);\n }\n return params;\n};\n\nexport const extractPaginationMeta = <T>(response: ZendeskListResponse<T>): PaginationMeta => ({\n has_more: response.meta?.has_more ?? response.next_page != null,\n after_cursor: response.meta?.after_cursor ?? null,\n count: response.count ?? 0,\n});\n\n// For search responses — offset-based, count is always present\nexport const extractSearchPaginationMeta = <T>(\n response: ZendeskListResponse<T>,\n perPage: number,\n page: number,\n): PaginationMeta => {\n const count = response.count ?? 0;\n const has_more = count > page * perPage;\n return {\n has_more,\n after_cursor: has_more ? String(page + 1) : null,\n count,\n };\n};\n","import * as z from 'zod/v4';\nimport {\n helpCenterGet,\n helpCenterPost,\n helpCenterPut,\n helpCenterUpload,\n zendeskGet,\n zendeskPost,\n} from '../client/zendesk-api';\nimport {\n DEFAULT_PAGE_SIZE,\n LARGE_ARTICLE_BODY_CHARS,\n LARGE_ARTICLE_SECTION_COUNT,\n MAX_PAGE_SIZE,\n} from '../constants';\nimport type {\n ZendeskArticle,\n ZendeskArticleAttachment,\n ZendeskCategory,\n ZendeskContentTag,\n ZendeskLabel,\n ZendeskListResponse,\n ZendeskPermissionGroup,\n ZendeskSection,\n ZendeskTranslation,\n ZendeskUserSegment,\n} from '../types';\nimport {\n htmlToMarkdown,\n markdownToHtml,\n parseSections,\n replaceSectionContent,\n} from '../utils/article-sections';\nimport {\n formatArticle,\n formatArticleSummary,\n formatAttachment,\n formatCategory,\n formatContentTag,\n formatLabel,\n formatList,\n formatPermissionGroup,\n formatSection,\n formatTranslation,\n formatTranslationSummary,\n formatUserSegment,\n truncateIfNeeded,\n} from '../utils/formatting';\nimport {\n buildCursorParams,\n buildOffsetParams,\n extractPaginationMeta,\n extractSearchPaginationMeta,\n} from '../utils/pagination';\nimport type { ToolContext, ToolDefinition } from './definitions';\n\nconst largeArticleHint = (body: string, sectionCount: number): string | null => {\n if (body.length < LARGE_ARTICLE_BODY_CHARS && sectionCount < LARGE_ARTICLE_SECTION_COUNT) {\n return null;\n }\n return [\n `> ⚠ Large article (${body.length} chars, ${sectionCount} sections).`,\n '> For targeted edits, prefer get_article_outline + get_article_section +',\n '> update_article_section to avoid re-sending the full body on each write.',\n '',\n ].join('\\n');\n};\n\nexport const createHelpCenterTools = (ctx: ToolContext): ToolDefinition[] => {\n const { subdomain, getToken } = ctx;\n\n return [\n {\n name: 'search_articles',\n namespace: 'help_center',\n readOnly: true,\n title: 'Search Help Center Articles',\n description:\n 'Full-text search across Help Center articles (metadata only, no body). Use get_article for full content. Supports locale filtering. Returns total count.',\n inputSchema: z.object({\n query: z.string().min(1).describe('Search query'),\n locale: z.string().optional().describe('Filter by locale (e.g., \"en-us\", \"fr\")'),\n per_page: z\n .number()\n .int()\n .min(1)\n .max(MAX_PAGE_SIZE)\n .default(DEFAULT_PAGE_SIZE)\n .describe('Results per page'),\n page: z.number().int().min(1).default(1).describe('Page number'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { query, locale, per_page, page } = params as {\n query: string;\n locale?: string;\n per_page: number;\n page: number;\n };\n const token = await getToken();\n const p: Record<string, string> = { query, ...buildOffsetParams(per_page, page) };\n if (locale) p['locale'] = locale;\n const response = await helpCenterGet<ZendeskListResponse<ZendeskArticle>>(\n subdomain,\n token,\n '/articles/search',\n p,\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.results ?? [],\n formatArticleSummary,\n extractSearchPaginationMeta(response, per_page, page),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'get_article',\n namespace: 'help_center',\n readOnly: true,\n title: 'Get Help Center Article',\n description:\n 'Retrieve an article by ID with full body content. For large articles, prefer get_article_outline + get_article_section to save tokens. Optionally specify locale for a translated version. Returns body (HTML), metadata, source_locale, and list of available translations.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n locale: z.string().optional().describe('Locale for translated version'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale } = params as { article_id: number; locale?: string };\n const token = await getToken();\n const path = locale ? `/${locale}/articles/${article_id}` : `/articles/${article_id}`;\n const { article } = await helpCenterGet<{ article: ZendeskArticle }>(\n subdomain,\n token,\n path,\n );\n const { translations } = await helpCenterGet<{ translations: ZendeskTranslation[] }>(\n subdomain,\n token,\n `/articles/${article_id}/translations`,\n );\n const hint = largeArticleHint(article.body, parseSections(article.body).length);\n const text =\n (hint ?? '') +\n formatArticle(article) +\n `\\n\\n**Available translations**: ${translations.map((t) => t.locale).join(', ')}`;\n return { content: [{ type: 'text', text: truncateIfNeeded(text) }] };\n },\n },\n {\n name: 'list_categories',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Help Center Categories',\n description: 'List all Help Center categories. Optionally filter by locale.',\n inputSchema: z.object({\n locale: z.string().optional(),\n page_size: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),\n cursor: z.string().optional(),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { locale, page_size, cursor } = params as {\n locale?: string;\n page_size: number;\n cursor?: string;\n };\n const token = await getToken();\n const path = locale ? `/${locale}/categories` : '/categories';\n const response = await helpCenterGet<ZendeskListResponse<ZendeskCategory>>(\n subdomain,\n token,\n path,\n buildCursorParams(page_size, cursor),\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.categories ?? [],\n formatCategory,\n extractPaginationMeta(response),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'list_sections',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Help Center Sections',\n description: 'List sections, optionally filtered by category ID and locale.',\n inputSchema: z.object({\n category_id: z.number().int().optional(),\n locale: z.string().optional(),\n page_size: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),\n cursor: z.string().optional(),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { category_id, locale, page_size, cursor } = params as {\n category_id?: number;\n locale?: string;\n page_size: number;\n cursor?: string;\n };\n const token = await getToken();\n const path =\n category_id && locale\n ? `/${locale}/categories/${category_id}/sections`\n : category_id\n ? `/categories/${category_id}/sections`\n : locale\n ? `/${locale}/sections`\n : '/sections';\n const response = await helpCenterGet<ZendeskListResponse<ZendeskSection>>(\n subdomain,\n token,\n path,\n buildCursorParams(page_size, cursor),\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.sections ?? [],\n formatSection,\n extractPaginationMeta(response),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'list_articles',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Help Center Articles',\n description:\n 'List articles (metadata only, no body). Use get_article for full content. Optionally filter by section ID and locale. Supports sort_by (\"title\", \"created_at\", \"updated_at\") and include_translations: true to show available translation locales per article. Note: include_translations must be re-sent on each paginated request.',\n inputSchema: z.object({\n section_id: z.number().int().optional(),\n locale: z.string().optional(),\n page_size: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),\n cursor: z.string().optional(),\n sort_by: z\n .enum(['created_at', 'updated_at', 'position', 'title'])\n .default('position')\n .describe('Sort field'),\n sort_order: z.enum(['asc', 'desc']).default('asc').describe('Sort direction'),\n include_translations: z\n .boolean()\n .default(false)\n .describe(\n 'Include available translation locales per article (causes 1 extra API call per article)',\n ),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { section_id, locale, page_size, cursor, sort_by, sort_order, include_translations } =\n params as {\n section_id?: number;\n locale?: string;\n page_size: number;\n cursor?: string;\n sort_by: string;\n sort_order: string;\n include_translations: boolean;\n };\n const token = await getToken();\n const path =\n section_id && locale\n ? `/${locale}/sections/${section_id}/articles`\n : section_id\n ? `/sections/${section_id}/articles`\n : locale\n ? `/${locale}/articles`\n : '/articles';\n const response = await helpCenterGet<ZendeskListResponse<ZendeskArticle>>(\n subdomain,\n token,\n path,\n { ...buildCursorParams(page_size, cursor), sort_by, sort_order },\n );\n const articles = response.articles ?? [];\n if (!include_translations) {\n return {\n content: [\n {\n type: 'text',\n text: formatList(articles, formatArticleSummary, extractPaginationMeta(response)),\n },\n ],\n };\n }\n const formatted = await Promise.all(\n articles.map(async (article) => {\n const { translations } = await helpCenterGet<{ translations: ZendeskTranslation[] }>(\n subdomain,\n token,\n `/articles/${article.id}/translations`,\n );\n const locales = translations.map((t) => t.locale).join(', ');\n return `${formatArticleSummary(article)}\\n- **Translations**: ${locales}`;\n }),\n );\n const meta = extractPaginationMeta(response);\n const header = meta.count\n ? `Results: ${meta.count}${meta.has_more ? ` | More available (cursor: ${meta.after_cursor})` : ''}`\n : '';\n const text = [header, ...formatted].filter(Boolean).join('\\n\\n');\n return { content: [{ type: 'text', text: truncateIfNeeded(text) }] };\n },\n },\n {\n name: 'list_article_translations',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Article Translations',\n description:\n 'List all available translations for an article (metadata only, no body: locale, title, draft, updated_at). Use get_article with locale for full translated content.',\n inputSchema: z.object({ article_id: z.number().int().describe('Article ID') }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id } = params as { article_id: number };\n const token = await getToken();\n const { translations } = await helpCenterGet<{ translations: ZendeskTranslation[] }>(\n subdomain,\n token,\n `/articles/${article_id}/translations`,\n );\n return {\n content: [{ type: 'text', text: formatList(translations, formatTranslationSummary) }],\n };\n },\n },\n {\n name: 'create_article_translation',\n namespace: 'help_center',\n readOnly: false,\n title: 'Create Article Translation',\n description: 'Create a translation for an existing article in a specific locale.',\n inputSchema: z.object({\n article_id: z.number().int(),\n locale: z.string().describe('Target locale (e.g., \"fr\", \"de\")'),\n title: z.string().min(1),\n body: z.string().min(1).describe('Translated body (HTML)'),\n draft: z.boolean().default(false),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale, title, body, draft } = params as {\n article_id: number;\n locale: string;\n title: string;\n body: string;\n draft: boolean;\n };\n const token = await getToken();\n const { translation } = await helpCenterPost<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations`,\n { translation: { locale, title, body, draft } },\n );\n return {\n content: [\n {\n type: 'text',\n text: `Translation created for article #${article_id} in \"${locale}\".\\n\\n${formatTranslation(translation)}`,\n },\n ],\n };\n },\n },\n {\n name: 'update_article_translation',\n namespace: 'help_center',\n readOnly: false,\n title: 'Update Article Translation',\n description:\n \"Update article content (title, body) in a specific locale. For targeted edits on one or a few sections, prefer update_article_section — this tool replaces the FULL body and re-sends the entire article on each write. Use the article's source_locale (from get_article) for the default language, or another locale for translations.\",\n inputSchema: z.object({\n article_id: z.number().int(),\n locale: z.string(),\n title: z.string().optional(),\n body: z.string().optional(),\n draft: z.boolean().optional(),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale, ...updates } = params as {\n article_id: number;\n locale: string;\n } & Record<string, unknown>;\n const token = await getToken();\n const { translation } = await helpCenterPut<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${locale}`,\n { translation: updates },\n );\n return {\n content: [\n {\n type: 'text',\n text: `Translation updated for article #${article_id} in \"${locale}\".\\n\\n${formatTranslation(translation)}`,\n },\n ],\n };\n },\n },\n {\n name: 'list_permission_groups',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Permission Groups',\n description:\n 'List all Guide permission groups. Use this to find the permission_group_id required when creating articles.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async () => {\n const token = await getToken();\n const response = await zendeskGet<{\n permission_groups: ZendeskPermissionGroup[];\n count: number;\n }>(subdomain, token, '/guide/permission_groups');\n return {\n content: [\n {\n type: 'text',\n text: formatList(response.permission_groups ?? [], formatPermissionGroup),\n },\n ],\n };\n },\n },\n {\n name: 'create_article',\n namespace: 'help_center',\n readOnly: false,\n title: 'Create Help Center Article',\n description:\n \"Create a new article in a section. The locale becomes the article's source_locale. Requires a permission_group_id (use list_permission_groups to find available IDs). To add content in other locales afterwards, use create_article_translation.\",\n inputSchema: z.object({\n section_id: z.number().int(),\n title: z.string().min(1),\n body: z.string().min(1).describe('Article body (HTML)'),\n permission_group_id: z\n .number()\n .int()\n .describe('Permission group ID (use list_permission_groups to find it)'),\n user_segment_id: z\n .number()\n .int()\n .optional()\n .describe(\n 'User segment ID for visibility (use list_user_segments to find it). Defaults to everyone.',\n ),\n author_id: z\n .number()\n .int()\n .optional()\n .describe('Author user ID. Defaults to the authenticated user.'),\n content_tag_ids: z\n .array(z.string())\n .optional()\n .describe('Content tag IDs (use list_content_tags to find them)'),\n locale: z.string().optional(),\n draft: z.boolean().default(true),\n promoted: z.boolean().default(false),\n label_names: z\n .array(z.string())\n .optional()\n .describe('Label names for search ranking (use list_labels to see existing labels)'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { section_id, ...articleData } = params as { section_id: number } & Record<\n string,\n unknown\n >;\n const token = await getToken();\n const { article } = await helpCenterPost<{ article: ZendeskArticle }>(\n subdomain,\n token,\n `/sections/${section_id}/articles`,\n { article: articleData },\n );\n return {\n content: [\n { type: 'text', text: `Article #${article.id} created.\\n\\n${formatArticle(article)}` },\n ],\n };\n },\n },\n {\n name: 'update_article',\n namespace: 'help_center',\n readOnly: false,\n title: 'Update Help Center Article',\n description:\n 'Update article metadata only (draft, promoted, labels, tags, visibility, section, sort position, etc.). Does NOT update content (title, body) — use update_article_translation for that.',\n inputSchema: z.object({\n article_id: z.number().int(),\n draft: z.boolean().optional(),\n promoted: z.boolean().optional(),\n label_names: z.array(z.string()).optional().describe('Label names for search ranking'),\n content_tag_ids: z.array(z.string()).optional().describe('Content tag IDs'),\n user_segment_id: z.number().int().optional().describe('User segment ID for visibility'),\n author_id: z.number().int().optional().describe('Author user ID'),\n permission_group_id: z.number().int().optional().describe('Permission group ID'),\n section_id: z.number().int().optional(),\n position: z\n .number()\n .int()\n .min(0)\n .optional()\n .describe(\n 'Sort position within the section (manual ordering only; 0 = first/top). New articles default to position 0. To move an article to the END of its section, set this to one more than the highest current position: read the highest position P from list_articles with sort_by=\"position\", sort_order=\"desc\", then set position = P + 1.',\n ),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, ...updates } = params as { article_id: number } & Record<\n string,\n unknown\n >;\n const token = await getToken();\n const { article } = await helpCenterPut<{ article: ZendeskArticle }>(\n subdomain,\n token,\n `/articles/${article_id}`,\n { article: updates },\n );\n return {\n content: [\n { type: 'text', text: `Article #${article.id} updated.\\n\\n${formatArticle(article)}` },\n ],\n };\n },\n },\n {\n name: 'list_content_tags',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Content Tags',\n description:\n 'List all Guide content tags. Content tags are visible to end users and help them find related articles.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async () => {\n const token = await getToken();\n const response = await zendeskGet<{ records: ZendeskContentTag[]; count: number }>(\n subdomain,\n token,\n '/guide/content_tags',\n );\n return {\n content: [{ type: 'text', text: formatList(response.records ?? [], formatContentTag) }],\n };\n },\n },\n {\n name: 'create_content_tag',\n namespace: 'help_center',\n readOnly: false,\n title: 'Create Content Tag',\n description: 'Create a new content tag for Guide articles.',\n inputSchema: z.object({\n name: z.string().min(1).describe('Content tag name'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { name } = params as { name: string };\n const token = await getToken();\n const { content_tag } = await zendeskPost<{ content_tag: ZendeskContentTag }>(\n subdomain,\n token,\n '/guide/content_tags',\n { content_tag: { name } },\n );\n return {\n content: [\n { type: 'text', text: `Content tag created.\\n\\n${formatContentTag(content_tag)}` },\n ],\n };\n },\n },\n {\n name: 'list_labels',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Article Labels',\n description:\n 'List all article labels. Labels improve Help Center search ranking and are not visible to end users.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async () => {\n const token = await getToken();\n const response = await helpCenterGet<{ labels: ZendeskLabel[]; count: number }>(\n subdomain,\n token,\n '/articles/labels',\n );\n return {\n content: [{ type: 'text', text: formatList(response.labels ?? [], formatLabel) }],\n };\n },\n },\n {\n name: 'list_user_segments',\n namespace: 'help_center',\n readOnly: true,\n title: 'List User Segments',\n description:\n 'List all user segments. User segments control article visibility (who can view). Use the ID when creating or updating articles.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async () => {\n const token = await getToken();\n const response = await helpCenterGet<{\n user_segments: ZendeskUserSegment[];\n count: number;\n }>(subdomain, token, '/user_segments');\n return {\n content: [\n { type: 'text', text: formatList(response.user_segments ?? [], formatUserSegment) },\n ],\n };\n },\n },\n {\n name: 'list_article_attachments',\n namespace: 'help_center',\n readOnly: true,\n title: 'List Article Attachments',\n description: 'List all attachments for an article.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id } = params as { article_id: number };\n const token = await getToken();\n const response = await helpCenterGet<{\n article_attachments: ZendeskArticleAttachment[];\n count: number;\n }>(subdomain, token, `/articles/${article_id}/attachments`);\n return {\n content: [\n {\n type: 'text',\n text: formatList(response.article_attachments ?? [], formatAttachment),\n },\n ],\n };\n },\n },\n {\n name: 'get_article_outline',\n namespace: 'help_center',\n readOnly: true,\n title: 'Get Article Outline',\n description:\n 'Return a compact outline of an article (list of sections delimited by h1/h2/h3, with word counts) for the given locale (defaults to source_locale). Includes available translations with their outdated status. Use get_article_section to fetch a specific section.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n locale: z\n .string()\n .optional()\n .describe('Locale of the body to outline (defaults to article source_locale)'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale } = params as { article_id: number; locale?: string };\n const token = await getToken();\n const { article } = await helpCenterGet<{ article: ZendeskArticle }>(\n subdomain,\n token,\n `/articles/${article_id}`,\n );\n const effectiveLocale = locale ?? article.source_locale;\n const { translation } = await helpCenterGet<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${effectiveLocale}`,\n );\n const { translations } = await helpCenterGet<{\n translations: Array<ZendeskTranslation & { outdated?: boolean }>;\n }>(subdomain, token, `/articles/${article_id}/translations`);\n const sections = parseSections(translation.body);\n\n const outlineLines = sections.length\n ? sections\n .map(\n (s) =>\n `- [${s.index}] ${s.headingTag ? `${s.headingTag}: ` : ''}${s.heading} (${s.wordCount} words)`,\n )\n .join('\\n')\n : '_(no sections detected)_';\n const translationsList = translations\n .map((t) => `- ${t.locale}${t.outdated ? ' (outdated)' : ''}`)\n .join('\\n');\n\n const text = [\n `# Outline — Article #${article_id} (${effectiveLocale})`,\n `**Title**: ${translation.title}`,\n '',\n '## Sections',\n outlineLines,\n '',\n '## Available translations',\n translationsList,\n ].join('\\n');\n return { content: [{ type: 'text', text }] };\n },\n },\n {\n name: 'get_article_section',\n namespace: 'help_center',\n readOnly: true,\n title: 'Get Article Section',\n description:\n 'Retrieve the content of a single section of an article in a given locale. Use get_article_outline first to discover section indexes. Default format=\"html\" for round-trip safety. Pass format=\"markdown\" only for human review — the Markdown representation is lossy on some structures (<pre> with <br>, tables with multi-<p> cells are kept as raw HTML to limit the damage, but do not round-trip markdown content back through update_article_section).',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n locale: z.string().describe('Locale of the body (e.g., \"en-us\", \"fr\")'),\n section_index: z\n .number()\n .int()\n .min(0)\n .describe('0-based index of the section (see get_article_outline)'),\n format: z\n .enum(['html', 'markdown'])\n .default('html')\n .describe(\n 'Output format. \"html\" (default) is round-trip safe. \"markdown\" is lossy on some HTML structures — use only for human review, not before update_article_section.',\n ),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale, section_index, format } = params as {\n article_id: number;\n locale: string;\n section_index: number;\n format: 'html' | 'markdown';\n };\n const token = await getToken();\n const { translation } = await helpCenterGet<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${locale}`,\n );\n const sections = parseSections(translation.body);\n const section = sections[section_index];\n if (!section) {\n throw new Error(\n `Section index ${section_index} not found. Article has ${sections.length} section(s) (0-${Math.max(0, sections.length - 1)}).`,\n );\n }\n const content = format === 'markdown' ? htmlToMarkdown(section.html) : section.html;\n const headerLine = section.headingTag\n ? `## [${section.index}] ${section.headingTag}: ${section.heading}`\n : `## [${section.index}] ${section.heading}`;\n const text = [\n headerLine,\n `_Locale: ${locale} | Words: ${section.wordCount} | Format: ${format}_`,\n '',\n content,\n ].join('\\n');\n return { content: [{ type: 'text', text: truncateIfNeeded(text) }] };\n },\n },\n {\n name: 'update_article_section',\n namespace: 'help_center',\n readOnly: false,\n title: 'Update Article Section',\n description:\n 'Replace the content of a single section of an article in a given locale, keeping the rest of the body intact. The server fetches the current body, replaces the targeted section, and PUTs the full reconstructed body via the Translations API. Default format=\"html\" for fidelity. Use format=\"markdown\" only when you control the input and know it does not rely on structures that round-trip poorly (code blocks with line breaks, tables with multi-paragraph cells). The section heading is preserved and is NOT part of the replaced content.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n locale: z.string().describe('Locale of the translation to update'),\n section_index: z\n .number()\n .int()\n .min(0)\n .describe('0-based index of the section to replace (see get_article_outline)'),\n content: z\n .string()\n .describe(\n 'New content for the section (heading excluded). HTML by default, Markdown if format=\"markdown\".',\n ),\n format: z\n .enum(['html', 'markdown'])\n .default('html')\n .describe(\n 'Input format. \"html\" (default) is the safe path. \"markdown\" is converted to HTML server-side but may introduce artifacts on complex content.',\n ),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, locale, section_index, content, format } = params as {\n article_id: number;\n locale: string;\n section_index: number;\n content: string;\n format: 'html' | 'markdown';\n };\n const token = await getToken();\n const { translation } = await helpCenterGet<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${locale}`,\n );\n const newSectionHtml = format === 'markdown' ? markdownToHtml(content) : content;\n const newBody = replaceSectionContent(translation.body, section_index, newSectionHtml);\n const { translation: updated } = await helpCenterPut<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${locale}`,\n { translation: { body: newBody } },\n );\n const updatedSections = parseSections(updated.body);\n const updatedSection = updatedSections[section_index];\n const newWordCount = updatedSection?.wordCount ?? 0;\n const headingLabel = updatedSection?.heading ?? '(intro)';\n const text = [\n `Section [${section_index}] \"${headingLabel}\" updated for article #${article_id} (${locale}).`,\n `New word count: ${newWordCount}.`,\n ].join('\\n');\n return { content: [{ type: 'text', text }] };\n },\n },\n {\n name: 'compare_translations',\n namespace: 'help_center',\n readOnly: true,\n title: 'Compare Article Translations',\n description:\n 'Compare section structure between two locales of the same article, matched by index. Returns a compact table (one row per section) with status: \"ok\" (both present, source/target word count ratio within 25%), \"different\" (word count ratio diverges by more than 25% — size signal only, NOT a semantic divergence: two locales may legitimately differ in verbosity) or \"missing\" (section absent in target). Useful to spot structurally stale or missing sections; do not interpret \"different\" as an edit regression on its own.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n source_locale: z.string().describe('Source (reference) locale'),\n target_locale: z.string().describe('Target locale to compare against source'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, source_locale, target_locale } = params as {\n article_id: number;\n source_locale: string;\n target_locale: string;\n };\n const token = await getToken();\n const [sourceRes, targetRes] = await Promise.all([\n helpCenterGet<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${source_locale}`,\n ),\n helpCenterGet<{ translation: ZendeskTranslation }>(\n subdomain,\n token,\n `/articles/${article_id}/translations/${target_locale}`,\n ),\n ]);\n const sourceSections = parseSections(sourceRes.translation.body);\n const targetSections = parseSections(targetRes.translation.body);\n const maxLen = Math.max(sourceSections.length, targetSections.length);\n\n const rows: string[] = [];\n rows.push(`| Idx | Heading | Status | Source words | Target words |`);\n rows.push(`| --- | --- | --- | --- | --- |`);\n for (let i = 0; i < maxLen; i += 1) {\n const src = sourceSections[i];\n const tgt = targetSections[i];\n const heading = src?.heading ?? tgt?.heading ?? '';\n const sourceWords = src?.wordCount ?? 0;\n const targetWords = tgt?.wordCount ?? 0;\n let status: 'ok' | 'missing' | 'different';\n if (!tgt) status = 'missing';\n else if (!src) status = 'different';\n else {\n const denom = Math.max(sourceWords, 1);\n const diffRatio = Math.abs(sourceWords - targetWords) / denom;\n status = diffRatio > 0.25 ? 'different' : 'ok';\n }\n rows.push(`| ${i} | ${heading} | ${status} | ${sourceWords} | ${targetWords} |`);\n }\n\n const text = [\n `# Translation diff — Article #${article_id} (${source_locale} → ${target_locale})`,\n '',\n ...rows,\n ].join('\\n');\n return { content: [{ type: 'text', text }] };\n },\n },\n {\n name: 'create_article_attachment',\n namespace: 'help_center',\n readOnly: false,\n title: 'Create Article Attachment',\n description:\n 'Upload an attachment to an article. Provide file content as base64-encoded string.',\n inputSchema: z.object({\n article_id: z.number().int().describe('Article ID'),\n file_name: z.string().min(1).describe('File name (e.g., \"screenshot.png\")'),\n file_base64: z.string().min(1).describe('File content encoded as base64'),\n content_type: z\n .string()\n .default('application/octet-stream')\n .describe('MIME type (e.g., \"image/png\", \"application/pdf\")'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { article_id, file_name, file_base64, content_type } = params as {\n article_id: number;\n file_name: string;\n file_base64: string;\n content_type: string;\n };\n const token = await getToken();\n const buffer = Buffer.from(file_base64, 'base64');\n const blob = new Blob([buffer], { type: content_type });\n const formData = new FormData();\n formData.append('file', blob, file_name);\n const { article_attachment } = await helpCenterUpload<{\n article_attachment: ZendeskArticleAttachment;\n }>(subdomain, token, `/articles/${article_id}/attachments`, formData);\n return {\n content: [\n {\n type: 'text',\n text: `Attachment created for article #${article_id}.\\n\\n${formatAttachment(article_attachment)}`,\n },\n ],\n };\n },\n },\n ];\n};\n","import * as z from 'zod/v4';\nimport { zendeskGet } from '../client/zendesk-api';\nimport { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE } from '../constants';\nimport type { ZendeskListResponse } from '../types';\nimport { truncateIfNeeded } from '../utils/formatting';\nimport { buildOffsetParams, extractSearchPaginationMeta } from '../utils/pagination';\nimport type { ToolContext, ToolDefinition } from './definitions';\n\nconst formatSearchResult = (result: Record<string, unknown>): string => {\n const lines: string[] = [`## [${result['result_type']}] #${result['id']}`];\n if (result['subject']) lines.push(`**Subject**: ${result['subject']}`);\n if (result['name']) lines.push(`**Name**: ${result['name']}`);\n if (result['title']) lines.push(`**Title**: ${result['title']}`);\n if (result['email']) lines.push(`**Email**: ${result['email']}`);\n if (result['status']) lines.push(`**Status**: ${result['status']}`);\n if (result['description']) {\n const desc = String(result['description']);\n lines.push(desc.length > 200 ? `${desc.slice(0, 200)}...` : desc);\n }\n return lines.join('\\n');\n};\n\nexport const createSearchTools = (ctx: ToolContext): ToolDefinition[] => {\n const { subdomain, getToken } = ctx;\n\n return [\n {\n name: 'search',\n namespace: 'tickets',\n readOnly: true,\n title: 'Zendesk Unified Search',\n description:\n 'Search across tickets, users, and organizations. Supports filters like \"type:ticket status:open\", \"type:user role:agent\". Returns total count and paginated results (100 per page). Organization results include name and ID only — use get_organization for full details (tags, domains, details).',\n inputSchema: z.object({\n query: z.string().min(1).describe('Zendesk search query'),\n per_page: z\n .number()\n .int()\n .min(1)\n .max(MAX_PAGE_SIZE)\n .default(DEFAULT_PAGE_SIZE)\n .describe('Results per page (max 100)'),\n page: z.number().int().min(1).default(1).describe('Page number (1-based)'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { query, per_page, page } = params as {\n query: string;\n per_page: number;\n page: number;\n };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<Record<string, unknown>>>(\n subdomain,\n token,\n '/search',\n {\n query,\n ...buildOffsetParams(per_page, page),\n },\n );\n const results = response.results ?? [];\n const meta = extractSearchPaginationMeta(response, per_page, page);\n const header = `Total: ${meta.count} | Page ${page} (${results.length} results)${meta.has_more ? ` | Next page: ${meta.after_cursor}` : ''}`;\n const body = results.map(formatSearchResult).join('\\n\\n');\n return {\n content: [\n { type: 'text', text: truncateIfNeeded([header, body].filter(Boolean).join('\\n\\n')) },\n ],\n };\n },\n },\n ];\n};\n","import * as z from 'zod/v4';\nimport {\n fetchZendeskBinary,\n ZendeskApiError,\n zendeskGet,\n zendeskPost,\n zendeskPut,\n} from '../client/zendesk-api';\nimport {\n DEFAULT_PAGE_SIZE,\n MAX_ATTACHMENT_BYTES,\n MAX_COMMENT_PAGES,\n MAX_EMBEDDED_IMAGE_COUNT,\n MAX_PAGE_SIZE,\n} from '../constants';\nimport type {\n ZendeskComment,\n ZendeskListResponse,\n ZendeskTicket,\n ZendeskTicketAttachment,\n} from '../types';\nimport { formatComment, formatList, formatTicket, truncateIfNeeded } from '../utils/formatting';\nimport {\n buildCursorParams,\n buildOffsetParams,\n extractPaginationMeta,\n extractSearchPaginationMeta,\n} from '../utils/pagination';\nimport type { ToolContext, ToolDefinition, ToolImageContent, ToolTextContent } from './definitions';\n\nconst formatReference = (attachment: ZendeskTicketAttachment): string =>\n `**${attachment.file_name}** (id ${attachment.id}, ${attachment.content_type}, ${attachment.size} bytes) — ${attachment.content_url}`;\n\nconst buildEmbeddedImageBlocks = async (\n subdomain: string,\n token: string,\n attachment: ZendeskTicketAttachment,\n reference: string,\n): Promise<Array<ToolTextContent | ToolImageContent>> => {\n const { data, contentType } = await fetchZendeskBinary(subdomain, token, attachment.content_url);\n return [\n { type: 'image', data: data.toString('base64'), mimeType: contentType },\n { type: 'text', text: reference },\n ];\n};\n\n// Zendesk has no endpoint to list a ticket's attachments directly.\n// Attachments are always attached to comments, so the only way to collect\n// them all is to walk through every comment page and extract their attachments.\nconst fetchAllTicketComments = async (\n subdomain: string,\n token: string,\n ticketId: number,\n): Promise<ZendeskComment[]> => {\n const all: ZendeskComment[] = [];\n let cursor: string | undefined;\n let pages = 0;\n while (pages < MAX_COMMENT_PAGES) {\n const response = await zendeskGet<{\n comments: ZendeskComment[];\n meta?: { has_more: boolean; after_cursor: string };\n }>(subdomain, token, `/tickets/${ticketId}/comments`, buildCursorParams(MAX_PAGE_SIZE, cursor));\n all.push(...response.comments);\n pages += 1;\n if (!response.meta?.has_more || !response.meta?.after_cursor) break;\n cursor = response.meta.after_cursor;\n }\n return all;\n};\n\nconst collectAttachmentBlocks = async (\n subdomain: string,\n token: string,\n attachments: ZendeskTicketAttachment[],\n): Promise<Array<ToolTextContent | ToolImageContent>> => {\n const blocks: Array<ToolTextContent | ToolImageContent> = [];\n let embeddedCount = 0;\n\n for (const attachment of attachments) {\n const reference = formatReference(attachment);\n const isImage = attachment.content_type.startsWith('image/');\n\n if (!isImage) {\n blocks.push({ type: 'text', text: reference });\n continue;\n }\n\n let skipReason: string | null = null;\n if (attachment.size > MAX_ATTACHMENT_BYTES) {\n skipReason = 'skipped: exceeds 5 MB per-image limit';\n } else if (embeddedCount >= MAX_EMBEDDED_IMAGE_COUNT) {\n skipReason = `skipped: max ${MAX_EMBEDDED_IMAGE_COUNT} embedded images reached`;\n }\n\n if (skipReason) {\n blocks.push({ type: 'text', text: `${reference} — ${skipReason}` });\n continue;\n }\n\n try {\n blocks.push(...(await buildEmbeddedImageBlocks(subdomain, token, attachment, reference)));\n embeddedCount += 1;\n } catch (error) {\n const reason =\n error instanceof ZendeskApiError\n ? `download failed: ${error.status} ${error.statusText}`\n : 'download failed';\n blocks.push({ type: 'text', text: `${reference} — ${reason}` });\n }\n }\n\n return blocks;\n};\n\nexport const createTicketTools = (ctx: ToolContext): ToolDefinition[] => {\n const { subdomain, getToken } = ctx;\n\n return [\n {\n name: 'get_ticket',\n namespace: 'tickets',\n readOnly: true,\n title: 'Get Zendesk Ticket',\n description:\n 'Retrieve a Zendesk ticket by ID, including its comments if requested. Returns ticket details (subject, status, priority, assignee, tags, description) and optionally all comments/internal notes.',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n include_comments: z.boolean().default(false).describe('Include ticket comments'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, include_comments } = params as {\n ticket_id: number;\n include_comments: boolean;\n };\n const token = await getToken();\n const { ticket } = await zendeskGet<{ ticket: ZendeskTicket }>(\n subdomain,\n token,\n `/tickets/${ticket_id}`,\n );\n let text = formatTicket(ticket);\n if (include_comments) {\n const { comments } = await zendeskGet<{ comments: ZendeskComment[] }>(\n subdomain,\n token,\n `/tickets/${ticket_id}/comments`,\n );\n text += `\\n\\n---\\n# Comments\\n\\n${comments.map(formatComment).join('\\n\\n')}`;\n }\n return { content: [{ type: 'text', text: truncateIfNeeded(text) }] };\n },\n },\n {\n name: 'get_ticket_attachments',\n namespace: 'tickets',\n readOnly: true,\n title: 'Get Zendesk Ticket Attachments',\n description:\n 'Retrieve ticket attachments. Images are embedded inline; other files are listed as text references.',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n attachment_ids: z\n .array(z.number().int())\n .optional()\n .describe(\n 'Attachment IDs to fetch directly (e.g. extracted from a previous get_ticket(include_comments=true) call). When provided, skips the comments fetch entirely. When omitted, all attachments of the ticket are returned.',\n ),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, attachment_ids } = params as {\n ticket_id: number;\n attachment_ids?: number[];\n };\n const token = await getToken();\n\n let attachments: ZendeskTicketAttachment[];\n if (attachment_ids && attachment_ids.length > 0) {\n attachments = [];\n for (const id of attachment_ids) {\n try {\n const { attachment } = await zendeskGet<{ attachment: ZendeskTicketAttachment }>(\n subdomain,\n token,\n `/attachments/${id}`,\n );\n attachments.push(attachment);\n } catch (error) {\n if (!(error instanceof ZendeskApiError) || error.status !== 404) throw error;\n }\n }\n } else {\n const comments = await fetchAllTicketComments(subdomain, token, ticket_id);\n attachments = comments.flatMap((c) => c.attachments ?? []);\n }\n\n if (attachments.length === 0) {\n return {\n content: [{ type: 'text', text: `No attachments found on ticket #${ticket_id}.` }],\n };\n }\n const blocks = await collectAttachmentBlocks(subdomain, token, attachments);\n return {\n content: [\n {\n type: 'text',\n text: `# Attachments for ticket #${ticket_id} (${attachments.length} total)`,\n },\n ...blocks,\n ],\n };\n },\n },\n {\n name: 'search_tickets',\n namespace: 'tickets',\n readOnly: true,\n title: 'Search Zendesk Tickets',\n description:\n 'Search tickets using Zendesk query syntax (e.g., \"status:open assignee:me\", \"priority:urgent type:incident\"). Returns total count.',\n inputSchema: z.object({\n query: z.string().min(1).describe('Zendesk search query string'),\n per_page: z\n .number()\n .int()\n .min(1)\n .max(MAX_PAGE_SIZE)\n .default(DEFAULT_PAGE_SIZE)\n .describe('Results per page'),\n page: z.number().int().min(1).default(1).describe('Page number'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { query, per_page, page } = params as {\n query: string;\n per_page: number;\n page: number;\n };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<ZendeskTicket>>(\n subdomain,\n token,\n '/search',\n {\n query: `type:ticket ${query}`,\n ...buildOffsetParams(per_page, page),\n },\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.results ?? [],\n formatTicket,\n extractSearchPaginationMeta(response, per_page, page),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'create_ticket',\n namespace: 'tickets',\n readOnly: false,\n title: 'Create Zendesk Ticket',\n description:\n 'Create a new Zendesk support ticket with subject, description, and optional priority/type/assignee/tags.',\n inputSchema: z.object({\n subject: z.string().min(1).describe('Ticket subject'),\n description: z.string().min(1).describe('Ticket description'),\n priority: z.enum(['urgent', 'high', 'normal', 'low']).optional(),\n type: z.enum(['problem', 'incident', 'question', 'task']).optional(),\n assignee_id: z.number().int().optional(),\n group_id: z.number().int().optional(),\n tags: z.array(z.string()).optional(),\n custom_fields: z.array(z.object({ id: z.number().int(), value: z.unknown() })).optional(),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { subject, description, ...rest } = params as Record<string, unknown>;\n const token = await getToken();\n const { ticket } = await zendeskPost<{ ticket: ZendeskTicket }>(\n subdomain,\n token,\n '/tickets',\n {\n ticket: { subject, comment: { body: description }, ...rest },\n },\n );\n return {\n content: [\n { type: 'text', text: `Ticket #${ticket.id} created.\\n\\n${formatTicket(ticket)}` },\n ],\n };\n },\n },\n {\n name: 'update_ticket',\n namespace: 'tickets',\n readOnly: false,\n title: 'Update Zendesk Ticket',\n description:\n 'Update an existing ticket (status, priority, type, assignee, group, subject, tags, custom fields).',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n status: z.enum(['new', 'open', 'pending', 'hold', 'solved', 'closed']).optional(),\n priority: z.enum(['urgent', 'high', 'normal', 'low']).optional(),\n type: z.enum(['problem', 'incident', 'question', 'task']).optional(),\n assignee_id: z.number().int().optional(),\n group_id: z.number().int().optional(),\n subject: z.string().optional(),\n tags: z.array(z.string()).optional(),\n custom_fields: z.array(z.object({ id: z.number().int(), value: z.unknown() })).optional(),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, ...updates } = params as { ticket_id: number } & Record<string, unknown>;\n const token = await getToken();\n const { ticket } = await zendeskPut<{ ticket: ZendeskTicket }>(\n subdomain,\n token,\n `/tickets/${ticket_id}`,\n { ticket: updates },\n );\n return {\n content: [\n { type: 'text', text: `Ticket #${ticket.id} updated.\\n\\n${formatTicket(ticket)}` },\n ],\n };\n },\n },\n {\n name: 'add_private_note',\n namespace: 'tickets',\n readOnly: false,\n title: 'Add Private Note',\n description: 'Add an internal note (not visible to requester) to a ticket.',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n body: z.string().min(1).describe('Note content'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, body } = params as { ticket_id: number; body: string };\n const token = await getToken();\n await zendeskPut(subdomain, token, `/tickets/${ticket_id}`, {\n ticket: { comment: { body, public: false } },\n });\n return { content: [{ type: 'text', text: `Private note added to ticket #${ticket_id}.` }] };\n },\n },\n {\n name: 'add_public_comment',\n namespace: 'tickets',\n readOnly: false,\n title: 'Add Public Comment',\n description: 'Add a public comment (visible to requester) to a ticket.',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n body: z.string().min(1).describe('Comment content'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: false,\n idempotentHint: false,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, body } = params as { ticket_id: number; body: string };\n const token = await getToken();\n await zendeskPut(subdomain, token, `/tickets/${ticket_id}`, {\n ticket: { comment: { body, public: true } },\n });\n return {\n content: [{ type: 'text', text: `Public comment added to ticket #${ticket_id}.` }],\n };\n },\n },\n {\n name: 'list_tickets',\n namespace: 'tickets',\n readOnly: true,\n title: 'List Zendesk Tickets',\n description: 'List tickets with cursor-based pagination, sorted by most recently updated.',\n inputSchema: z.object({\n page_size: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),\n cursor: z.string().optional().describe('Pagination cursor'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { page_size, cursor } = params as { page_size: number; cursor?: string };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<ZendeskTicket>>(\n subdomain,\n token,\n '/tickets',\n buildCursorParams(page_size, cursor),\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.tickets ?? [],\n formatTicket,\n extractPaginationMeta(response),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'get_linked_incidents',\n namespace: 'tickets',\n readOnly: true,\n title: 'Get Linked Incidents',\n description: 'Get all incident tickets linked to a problem ticket.',\n inputSchema: z.object({\n problem_id: z.number().int().describe('Problem ticket ID'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { problem_id } = params as { problem_id: number };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<ZendeskTicket>>(\n subdomain,\n token,\n `/tickets/${problem_id}/incidents`,\n );\n const incidents = response.tickets ?? [];\n const text =\n incidents.length > 0\n ? `# Incidents linked to problem #${problem_id}\\n\\n${incidents.map(formatTicket).join('\\n\\n')}`\n : `No incidents linked to problem #${problem_id}.`;\n return { content: [{ type: 'text', text: truncateIfNeeded(text) }] };\n },\n },\n {\n name: 'manage_tags',\n namespace: 'tickets',\n readOnly: false,\n title: 'Manage Ticket Tags',\n description: 'Add or remove tags on a ticket.',\n inputSchema: z.object({\n ticket_id: z.number().int().describe('Ticket ID'),\n add: z.array(z.string()).optional().describe('Tags to add'),\n remove: z.array(z.string()).optional().describe('Tags to remove'),\n }),\n annotations: {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { ticket_id, add, remove } = params as {\n ticket_id: number;\n add?: string[];\n remove?: string[];\n };\n const token = await getToken();\n const { ticket } = await zendeskGet<{ ticket: ZendeskTicket }>(\n subdomain,\n token,\n `/tickets/${ticket_id}`,\n );\n const tags = new Set(ticket.tags);\n add?.forEach((t) => {\n tags.add(t);\n });\n remove?.forEach((t) => {\n tags.delete(t);\n });\n const { ticket: updated } = await zendeskPut<{ ticket: ZendeskTicket }>(\n subdomain,\n token,\n `/tickets/${ticket_id}`,\n { ticket: { tags: [...tags] } },\n );\n return {\n content: [\n {\n type: 'text',\n text: `Tags updated on ticket #${ticket_id}. Current: ${updated.tags.join(', ') || 'none'}`,\n },\n ],\n };\n },\n },\n ];\n};\n","import * as z from 'zod/v4';\nimport { zendeskGet } from '../client/zendesk-api';\nimport { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE } from '../constants';\nimport type { ZendeskListResponse, ZendeskOrganization, ZendeskUser } from '../types';\nimport { formatList, formatOrganization, formatUser } from '../utils/formatting';\nimport {\n buildCursorParams,\n buildOffsetParams,\n extractPaginationMeta,\n extractSearchPaginationMeta,\n} from '../utils/pagination';\nimport type { ToolContext, ToolDefinition } from './definitions';\n\nexport const createUserTools = (ctx: ToolContext): ToolDefinition[] => {\n const { subdomain, getToken } = ctx;\n\n return [\n {\n name: 'get_current_user',\n namespace: 'users',\n readOnly: true,\n title: 'Get Current Zendesk User',\n description:\n 'Get the currently authenticated Zendesk user. Useful to verify identity and permissions.',\n inputSchema: z.object({}),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async () => {\n const token = await getToken();\n const { user } = await zendeskGet<{ user: ZendeskUser }>(subdomain, token, '/users/me');\n return { content: [{ type: 'text', text: formatUser(user) }] };\n },\n },\n {\n name: 'search_users',\n namespace: 'users',\n readOnly: true,\n title: 'Search Zendesk Users',\n description:\n 'Search for users by name, email, or other criteria using Zendesk search query syntax. Returns total count.',\n inputSchema: z.object({\n query: z.string().min(1).describe('Search query'),\n per_page: z\n .number()\n .int()\n .min(1)\n .max(MAX_PAGE_SIZE)\n .default(DEFAULT_PAGE_SIZE)\n .describe('Results per page'),\n page: z.number().int().min(1).default(1).describe('Page number'),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { query, per_page, page } = params as {\n query: string;\n per_page: number;\n page: number;\n };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<ZendeskUser>>(\n subdomain,\n token,\n '/search',\n {\n query: `type:user ${query}`,\n ...buildOffsetParams(per_page, page),\n },\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.results ?? [],\n formatUser,\n extractSearchPaginationMeta(response, per_page, page),\n ),\n },\n ],\n };\n },\n },\n {\n name: 'get_user',\n namespace: 'users',\n readOnly: true,\n title: 'Get Zendesk User',\n description: 'Retrieve a user by ID.',\n inputSchema: z.object({ user_id: z.number().int().describe('User ID') }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { user_id } = params as { user_id: number };\n const token = await getToken();\n const { user } = await zendeskGet<{ user: ZendeskUser }>(\n subdomain,\n token,\n `/users/${user_id}`,\n );\n return { content: [{ type: 'text', text: formatUser(user) }] };\n },\n },\n {\n name: 'get_organization',\n namespace: 'users',\n readOnly: true,\n title: 'Get Zendesk Organization',\n description: 'Retrieve an organization by ID.',\n inputSchema: z.object({ organization_id: z.number().int().describe('Organization ID') }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { organization_id } = params as { organization_id: number };\n const token = await getToken();\n const { organization } = await zendeskGet<{ organization: ZendeskOrganization }>(\n subdomain,\n token,\n `/organizations/${organization_id}`,\n );\n return { content: [{ type: 'text', text: formatOrganization(organization) }] };\n },\n },\n {\n name: 'list_organizations',\n namespace: 'users',\n readOnly: true,\n title: 'List Zendesk Organizations',\n description: 'List all organizations with pagination.',\n inputSchema: z.object({\n page_size: z.number().int().min(1).max(MAX_PAGE_SIZE).default(DEFAULT_PAGE_SIZE),\n cursor: z.string().optional(),\n }),\n annotations: {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n },\n handler: async (params) => {\n const { page_size, cursor } = params as { page_size: number; cursor?: string };\n const token = await getToken();\n const response = await zendeskGet<ZendeskListResponse<ZendeskOrganization>>(\n subdomain,\n token,\n '/organizations',\n buildCursorParams(page_size, cursor),\n );\n return {\n content: [\n {\n type: 'text',\n text: formatList(\n response.organizations ?? [],\n formatOrganization,\n extractPaginationMeta(response),\n ),\n },\n ],\n };\n },\n },\n ];\n};\n","import type { ToolContext, ToolDefinition } from './definitions';\nimport { createHelpCenterTools } from './help-center';\nimport { createSearchTools } from './search';\nimport { createTicketTools } from './tickets';\nimport { createUserTools } from './users';\n\nexport type { ToolContext, ToolDefinition } from './definitions';\n\nexport const createAllTools = (ctx: ToolContext): ToolDefinition[] => [\n ...createTicketTools(ctx),\n ...createSearchTools(ctx),\n ...createHelpCenterTools(ctx),\n ...createUserTools(ctx),\n];\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport * as z from 'zod/v4';\nimport { ZendeskApiError } from './client/zendesk-api';\nimport type { Config } from './config';\nimport { filterTools, groupByNamespace } from './routing/registry';\nimport type { ToolAnnotations, ToolResult } from './tools/definitions';\nimport { createAllTools, type ToolDefinition } from './tools/index';\nimport { type Logger, silentLogger } from './utils/logger';\nimport { readPackageInfo } from './utils/package-info';\n\n/**\n * Invoke a tool handler, notifying `onUnauthorized` when Zendesk rejects the\n * token (401). This lets the OAuth store drop the dead token so the next call\n * refreshes/re-authenticates instead of replaying a revoked token. A no-op\n * callback (API-token mode) leaves behavior unchanged.\n */\nconst runHandler = async (\n def: ToolDefinition,\n params: Record<string, unknown>,\n onUnauthorized: (() => void) | undefined,\n): Promise<ToolResult> => {\n try {\n return await def.handler(params);\n } catch (err) {\n if (onUnauthorized && err instanceof ZendeskApiError && err.status === 401) {\n onUnauthorized();\n }\n throw err;\n }\n};\n\nconst NAMESPACE_LABELS: Record<string, { toolName: string; title: string }> = {\n tickets: { toolName: 'zendesk_tickets', title: 'Zendesk Tickets' },\n help_center: { toolName: 'zendesk_help_center', title: 'Zendesk Help Center' },\n users: { toolName: 'zendesk_users', title: 'Zendesk Users' },\n};\n\n// Keep proxy descriptions compact: a proxy tool concatenates one line per\n// sub-operation, so only the first sentence of each tool description is\n// included. Clients still receive the full schema via the wrapped tool.\nexport const summarizeDescription = (description: string): string => {\n const idx = description.indexOf('. ');\n if (idx === -1) return description;\n return description.slice(0, idx + 1);\n};\n\nexport const buildOperationList = (\n tools: ReadonlyArray<Pick<ToolDefinition, 'name' | 'description' | 'readOnly'>>,\n): string =>\n tools\n .map(\n (t) =>\n `- **${t.name}**: ${summarizeDescription(t.description)}${t.readOnly ? '' : ' (write)'}`,\n )\n .join('\\n');\n\n// A proxy aggregates N sub-operations. Hints follow the safest plausible\n// reading: readOnly/idempotent only if EVERY op is, destructive as soon as\n// ANY op is. openWorld is always true (we always hit Zendesk).\n// Mistral/Vibe ignore annotations entirely, hence the `[RO]` prefix below.\nexport const aggregateAnnotations = (\n tools: ReadonlyArray<Pick<ToolDefinition, 'annotations'>>,\n): ToolAnnotations => ({\n readOnlyHint: tools.every((t) => t.annotations.readOnlyHint),\n destructiveHint: tools.some((t) => t.annotations.destructiveHint),\n idempotentHint: tools.every((t) => t.annotations.idempotentHint),\n openWorldHint: true,\n});\n\ntype ProxyDispatch = (args: Record<string, unknown>) => Promise<ToolResult>;\n\n// Each proxy carries its OWN handler map, scoped to the operations it\n// advertises. In `namespace` mode this is essential: without it, a caller\n// could invoke `zendesk_tickets` with operation=\"get_article\" and dispatch\n// a help-center handler via a shared global map. The description would lie\n// but the call would still succeed.\nexport const buildProxyDispatch = (\n tools: ToolDefinition[],\n onUnauthorized: (() => void) | undefined,\n): ProxyDispatch => {\n const operationNames = tools.map((t) => t.name);\n const localHandlers = new Map<string, ToolDefinition>(tools.map((t) => [t.name, t]));\n\n return async (args) => {\n const { operation, params } = args as {\n operation: string;\n params: Record<string, unknown>;\n };\n const def = localHandlers.get(operation);\n if (!def) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Unknown operation \"${operation}\". Available: ${operationNames.join(', ')}`,\n },\n ],\n };\n }\n const validated = def.inputSchema.parse(params);\n return runHandler(def, validated, onUnauthorized);\n };\n};\n\nconst registerProxyTool = (\n server: McpServer,\n toolName: string,\n title: string,\n tools: ToolDefinition[],\n readOnlyMode: boolean,\n onUnauthorized: (() => void) | undefined,\n): void => {\n const operationNames = tools.map((t) => t.name);\n const operationList = buildOperationList(tools);\n const annotations = aggregateAnnotations(tools);\n const prefix = readOnlyMode ? '[RO] ' : '';\n\n const dispatch = buildProxyDispatch(tools, onUnauthorized);\n\n server.registerTool(\n toolName,\n {\n title,\n description: `${prefix}${title}. Specify the operation and its parameters.\\n\\nAvailable operations:\\n${operationList}`,\n inputSchema: {\n operation: z.string().describe(`One of: ${operationNames.join(', ')}`),\n params: z.record(z.string(), z.unknown()).default({}).describe('Operation parameters'),\n },\n annotations,\n },\n async (args) => dispatch(args as Record<string, unknown>),\n );\n};\n\nexport const createMcpServer = (\n config: Config,\n getToken: () => string | Promise<string>,\n logger: Logger = silentLogger,\n // Called when a tool handler hits a 401 from Zendesk (OAuth mode only). Lets\n // the token store invalidate the rejected token. Omitted in API-token mode.\n onUnauthorized?: () => void,\n): McpServer => {\n // Read name/version from package.json at runtime rather than hardcoding them\n // (the old literals were stale and even carried the wrong package name).\n const pkg = readPackageInfo();\n const server = new McpServer(\n {\n name: pkg.name,\n version: pkg.version,\n },\n // Advertise the logging capability so structured diagnostics (notably the\n // OAuth browser flow) reach clients that support it. Clients that don't\n // simply ignore the notifications.\n { capabilities: { logging: {} } },\n );\n\n // Route the logger's MCP sink through this server. Auth runs lazily on the\n // first tool call (after connect), so notifications can flow by then.\n logger.attachServer(server);\n\n const allTools = createAllTools({ subdomain: config.subdomain, getToken });\n\n // Apply filters (--read-only, --namespace, --tool)\n const filteredTools = filterTools(allTools, {\n readOnly: config.readOnly,\n namespaces: config.namespaces,\n tools: config.tools,\n });\n\n switch (config.mode) {\n case 'all': {\n for (const tool of filteredTools) {\n server.registerTool(\n tool.name,\n {\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema.shape,\n annotations: tool.annotations,\n },\n async (params) => runHandler(tool, params as Record<string, unknown>, onUnauthorized),\n );\n }\n break;\n }\n case 'namespace': {\n const grouped = groupByNamespace(filteredTools);\n for (const [namespace, tools] of grouped) {\n const label = NAMESPACE_LABELS[namespace];\n if (label) {\n registerProxyTool(\n server,\n label.toolName,\n label.title,\n tools,\n config.readOnly,\n onUnauthorized,\n );\n }\n }\n break;\n }\n case 'single': {\n registerProxyTool(\n server,\n 'zendesk',\n 'Zendesk',\n filteredTools,\n config.readOnly,\n onUnauthorized,\n );\n break;\n }\n }\n\n logger.info('tools_registered', { count: filteredTools.length, mode: config.mode });\n return server;\n};\n","import { randomUUID } from 'node:crypto';\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport type { Config } from '../config';\nimport { getOAuthUrls } from '../constants';\nimport { createMcpServer } from '../server';\nimport { type Logger, silentLogger } from '../utils/logger';\n\nconst WILDCARD_HOSTS = new Set(['0.0.0.0', '::', '*']);\n\n// Default CORS allowlist: the major web MCP clients that work today via\n// Custom Connector UIs. Native clients (Claude Desktop, Claude Code CLI,\n// Cursor, VS Code, Zed) send no Origin header — they're unaffected. Extend\n// the list with --cors-origin / CORS_ORIGIN per deployment.\n//\n// Ordered by current usage share (ChatGPT first). Update over time as the\n// MCP client landscape evolves.\nexport const DEFAULT_BROWSER_MCP_CLIENT_ORIGINS: ReadonlyArray<string> = [\n 'https://chatgpt.com',\n 'https://chat.openai.com',\n 'https://claude.ai',\n 'https://gemini.google.com',\n 'https://copilot.microsoft.com',\n 'https://www.perplexity.ai',\n 'https://chat.mistral.ai',\n 'https://grok.com',\n];\n\n// Methods + headers exposed across CORS for the /mcp endpoint. Headers list\n// covers what the SDK's StreamableHTTPClientTransport sets plus the bearer.\nconst CORS_ALLOWED_METHODS = 'GET, POST, DELETE, OPTIONS';\nconst CORS_ALLOWED_HEADERS =\n 'Authorization, Content-Type, Accept, mcp-session-id, mcp-protocol-version, last-event-id';\n// Clients need to read the session ID from the response to send it on\n// subsequent requests; without this header they can't.\nconst CORS_EXPOSE_HEADERS = 'mcp-session-id';\nconst CORS_MAX_AGE = '600';\n\nconst LOCALHOST_HOSTNAMES = new Set(['localhost', '127.0.0.1', '[::1]']);\nconst ALLOWED_PROTOCOLS = new Set(['http:', 'https:']);\n\n/**\n * Returns the origin string to reflect in `Access-Control-Allow-Origin`, or\n * `undefined` if the origin is not allowed.\n *\n * The returned value is **never the raw `Origin` request header**. It comes\n * from one of three sanitization points:\n *\n * 1. An entry of the hardcoded `DEFAULT_BROWSER_MCP_CLIENT_ORIGINS` array.\n * 2. An entry of the operator-configured `extraOrigins` array.\n * 3. A loopback origin rebuilt from validated URL components after the\n * hostname has been allowlisted against `LOCALHOST_HOSTNAMES`.\n *\n * This shape keeps the dataflow from request header to response header\n * gated by a constant allowlist, which is the pattern CodeQL's\n * `js/cors-misconfiguration-for-credentials` rule recognises as safe when\n * combined with `Access-Control-Allow-Credentials: true`.\n */\nexport const resolveAllowedOrigin = (\n origin: string,\n extraOrigins: ReadonlyArray<string> | undefined,\n): string | undefined => {\n const defaultMatch = DEFAULT_BROWSER_MCP_CLIENT_ORIGINS.find((entry) => entry === origin);\n if (defaultMatch) return defaultMatch;\n\n const extraMatch = extraOrigins?.find((entry) => entry === origin);\n if (extraMatch) return extraMatch;\n\n // Loopback on any port: parse, allowlist the hostname, then rebuild from\n // the validated parts. The output never includes raw header bytes.\n try {\n const url = new URL(origin);\n if (!ALLOWED_PROTOCOLS.has(url.protocol)) return undefined;\n if (!LOCALHOST_HOSTNAMES.has(url.hostname)) return undefined;\n const port = url.port || (url.protocol === 'https:' ? '443' : '80');\n return `${url.protocol}//${url.hostname}:${port}`;\n } catch {\n return undefined;\n }\n};\n\nconst applyCorsHeaders = (\n req: IncomingMessage,\n res: ServerResponse,\n extraOrigins: ReadonlyArray<string>,\n): void => {\n const requestOrigin = req.headers['origin'];\n if (typeof requestOrigin !== 'string' || requestOrigin.length === 0) return;\n\n // The value passed to setHeader is the matched entry from the constant\n // allowlist (or a loopback origin rebuilt from validated URL parts) —\n // never the raw request header. This satisfies CodeQL's\n // `js/cors-misconfiguration-for-credentials` rule when combined with\n // Access-Control-Allow-Credentials: true.\n const allowedOrigin = resolveAllowedOrigin(requestOrigin, extraOrigins);\n if (!allowedOrigin) return;\n\n res.setHeader('Access-Control-Allow-Origin', allowedOrigin);\n res.setHeader('Vary', 'Origin');\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n res.setHeader('Access-Control-Expose-Headers', CORS_EXPOSE_HEADERS);\n};\n\nconst handleCorsPreflight = (\n req: IncomingMessage,\n res: ServerResponse,\n extraOrigins: ReadonlyArray<string>,\n): boolean => {\n if (req.method !== 'OPTIONS') return false;\n applyCorsHeaders(req, res, extraOrigins);\n // Preflight needs Allow-Methods and Allow-Headers in addition to the basic\n // CORS headers; if the origin isn't allowlisted, applyCorsHeaders is a\n // no-op and we still 204 (the browser blocks based on missing ACAO).\n if (res.getHeader('Access-Control-Allow-Origin')) {\n res.setHeader('Access-Control-Allow-Methods', CORS_ALLOWED_METHODS);\n res.setHeader('Access-Control-Allow-Headers', CORS_ALLOWED_HEADERS);\n res.setHeader('Access-Control-Max-Age', CORS_MAX_AGE);\n }\n res.writeHead(204);\n res.end();\n return true;\n};\n\n// Build the canonical `resource` URL we advertise in the OAuth metadata.\n// Precedence:\n// 1. Explicit --public-url / PUBLIC_URL (operators behind a reverse proxy\n// must set this; Azure App Service: PUBLIC_URL=\"https://${WEBSITE_HOSTNAME}\").\n// 2. host:port when host is a real, routable hostname or IP (not the bind\n// wildcard 0.0.0.0 / :: / unspecified).\n// 3. Fallback to host:port + warning. Clients following RFC 8707 strictly\n// will reject this resource identifier, so log a clear warning so the\n// operator knows to set PUBLIC_URL.\nexport const resolveResourceUrl = (config: Config, logger: Logger = silentLogger): string => {\n if (config.publicUrl) return config.publicUrl.replace(/\\/+$/, '');\n if (!WILDCARD_HOSTS.has(config.host)) {\n return `http://${config.host}:${config.port}`;\n }\n logger.warn('public_url_unset', {\n host: config.host,\n advertised: `http://${config.host}:${config.port}`,\n hint:\n 'OAuth discovery will advertise a non-routable resource identifier and spec-compliant ' +\n 'MCP clients may refuse the connection. Set PUBLIC_URL (or --public-url) to the URL ' +\n 'clients use to reach this server (e.g. https://your-host.example.com).',\n });\n return `http://${config.host}:${config.port}`;\n};\n\n// Error message must stay ASCII-only: it gets embedded in the WWW-Authenticate\n// response header on 401, and node:http's setHeader rejects non-ASCII bytes\n// with ERR_INVALID_CHAR (which would surface as a 500 instead of the\n// spec-required 401).\nconst MISSING_BEARER_MESSAGE =\n 'Missing Authorization: Bearer <zendesk-oauth-token> header. ' +\n 'HTTP mode requires per-user OAuth 2.1 PKCE - obtain a token from Zendesk via your MCP client.';\n\nexport const extractBearer = (request: IncomingMessage): string | undefined => {\n const header = request.headers['authorization'];\n if (typeof header !== 'string') return undefined;\n if (!header.toLowerCase().startsWith('bearer ')) return undefined;\n return header.slice('bearer '.length).trim();\n};\n\ninterface OAuthMetadata {\n protectedResource: {\n authorization_servers: string[];\n resource: string;\n bearer_methods_supported: string[];\n scopes_supported: string[];\n };\n authorizationServer: {\n issuer: string;\n authorization_endpoint: string;\n token_endpoint: string;\n response_types_supported: string[];\n grant_types_supported: string[];\n code_challenge_methods_supported: string[];\n token_endpoint_auth_methods_supported: string[];\n scopes_supported: string[];\n };\n}\n\nexport const buildOAuthMetadata = (\n config: Config,\n logger: Logger = silentLogger,\n): OAuthMetadata => {\n const { authorizeUrl, tokenUrl } = getOAuthUrls(config.subdomain);\n const issuer = `https://${config.subdomain}.zendesk.com`;\n const resource = resolveResourceUrl(config, logger);\n return {\n protectedResource: {\n authorization_servers: [issuer],\n resource,\n bearer_methods_supported: ['header'],\n scopes_supported: ['read', 'write'],\n },\n authorizationServer: {\n issuer,\n authorization_endpoint: authorizeUrl,\n token_endpoint: tokenUrl,\n response_types_supported: ['code'],\n grant_types_supported: ['authorization_code', 'refresh_token'],\n code_challenge_methods_supported: ['S256'],\n token_endpoint_auth_methods_supported: ['none'],\n scopes_supported: ['read', 'write'],\n },\n };\n};\n\nconst sendJson = (res: ServerResponse, status: number, body: unknown): void => {\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(body));\n};\n\n// Single construction point for JSON-RPC-shaped HTTP errors so the envelope\n// ({ error, id: null, jsonrpc }) can't drift between the 4xx/5xx paths.\nconst sendJsonRpcError = (\n res: ServerResponse,\n status: number,\n code: number,\n message: string,\n headers: Record<string, string> = {},\n): void => {\n res.writeHead(status, { 'Content-Type': 'application/json', ...headers });\n res.end(JSON.stringify({ error: { code, message }, id: null, jsonrpc: '2.0' }));\n};\n\nconst sendUnauthorized = (res: ServerResponse, resource: string): void => {\n // RFC 6750 + MCP 2025-06-18: the WWW-Authenticate header points the client\n // at the resource metadata that bootstraps the OAuth discovery flow.\n const wwwAuthenticate = `Bearer resource_metadata=\"${resource}/.well-known/oauth-protected-resource\", error=\"invalid_token\", error_description=\"${MISSING_BEARER_MESSAGE}\"`;\n sendJsonRpcError(res, 401, -32000, MISSING_BEARER_MESSAGE, {\n 'WWW-Authenticate': wwwAuthenticate,\n });\n};\n\ninterface Session {\n transport: StreamableHTTPServerTransport;\n /**\n * Latest bearer presented for this session. Tool calls read through this\n * object so a client that refreshes its Zendesk token mid-session has the\n * fresh token used on the next call.\n */\n auth: { bearer: string };\n /** Epoch ms of the last request routed to this session (idle eviction). */\n lastActivityAt: number;\n close(): Promise<void>;\n}\n\n// JSON-RPC tool calls are small; this is a generous ceiling that exists only\n// so a client can't stream an unbounded body into server memory.\nexport const MAX_BODY_BYTES = 4 * 1024 * 1024;\n\ntype BodyResult =\n | { ok: true; value: unknown }\n | { ok: false; status: number; rpcCode: number; message: string };\n\n// Read and parse the request body. The SDK transport could parse the stream\n// itself, but it neither caps the body size nor maps parse failures to the\n// right HTTP status — buffering here keeps both concerns in one place.\nconst readJsonBody = (req: IncomingMessage, maxBodyBytes: number): Promise<BodyResult> =>\n new Promise((resolve) => {\n const chunks: Buffer[] = [];\n let total = 0;\n let settled = false;\n const settle = (result: BodyResult): void => {\n if (settled) return;\n settled = true;\n resolve(result);\n };\n req.on('data', (chunk: Buffer) => {\n total += chunk.length;\n if (total > maxBodyBytes) {\n req.removeAllListeners('data');\n settle({\n ok: false,\n status: 413,\n rpcCode: -32600,\n message: `Request body exceeds ${maxBodyBytes} bytes.`,\n });\n return;\n }\n chunks.push(chunk);\n });\n req.on('end', () => {\n const raw = Buffer.concat(chunks).toString('utf8');\n if (!raw) {\n settle({ ok: true, value: undefined });\n return;\n }\n try {\n settle({ ok: true, value: JSON.parse(raw) });\n } catch {\n settle({\n ok: false,\n status: 400,\n rpcCode: -32700,\n message: 'Parse error: request body is not valid JSON.',\n });\n }\n });\n req.on('error', () =>\n settle({\n ok: false,\n status: 400,\n rpcCode: -32600,\n message: 'Request body could not be read.',\n }),\n );\n });\n\nconst respondBodyError = (\n req: IncomingMessage,\n res: ServerResponse,\n failure: Extract<BodyResult, { ok: false }>,\n): void => {\n const headers = failure.status === 413 ? { Connection: 'close' } : {};\n sendJsonRpcError(res, failure.status, failure.rpcCode, failure.message, headers);\n if (failure.status === 413) {\n // The client may still be mid-upload with the request stream paused\n // (readJsonBody dropped its listeners at the cap). Left alone, the\n // connection wedges: the client blocks on backpressure and\n // httpServer.close() waits on the socket forever. Drop it as soon as the\n // 413 has flushed.\n if (res.writableFinished) req.destroy();\n else res.once('finish', () => req.destroy());\n }\n};\n\n// A client that vanishes without DELETEing its session would otherwise leak a\n// McpServer + bearer forever (the SDK only fires onclose on an explicit\n// DELETE). A periodic sweep closes sessions with no traffic for this long;\n// well-behaved clients simply re-initialize on their next request.\nconst SESSION_IDLE_TIMEOUT_MS = 30 * 60 * 1000;\nconst SESSION_SWEEP_INTERVAL_MS = 60 * 1000;\n\nexport interface HttpServerHandle {\n /** Actual bound port (resolved when listen completes — useful for port:0 tests). */\n port: number;\n /** Stop the HTTP server and tear down every active MCP session. */\n close(): Promise<void>;\n}\n\nexport interface HttpTransportOptions {\n /** Idle-session eviction timeout override (tests). */\n sessionIdleTimeoutMs?: number;\n /** Idle-session sweep interval override (tests). */\n sweepIntervalMs?: number;\n /** Request body cap override (tests use a small cap to stay cheap). */\n maxBodyBytes?: number;\n}\n\nexport const startHttpTransport = async (\n config: Config,\n logger: Logger = silentLogger,\n options: HttpTransportOptions = {},\n): Promise<HttpServerHandle> => {\n const metadata = buildOAuthMetadata(config, logger);\n const sessions = new Map<string, Session>();\n const idleTimeoutMs = options.sessionIdleTimeoutMs ?? SESSION_IDLE_TIMEOUT_MS;\n const maxBodyBytes = options.maxBodyBytes ?? MAX_BODY_BYTES;\n\n const handleMcpRequest = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n // MCP 2025-06-18: the Authorization header is validated on EVERY request,\n // not only at session initialization — a session id alone is not a\n // credential. An unauthenticated request gets a 401 + WWW-Authenticate\n // that bootstraps the OAuth flow on the client side.\n const bearer = extractBearer(req);\n if (!bearer) {\n sendUnauthorized(res, metadata.protectedResource.resource);\n return;\n }\n\n const sessionId =\n typeof req.headers['mcp-session-id'] === 'string' ? req.headers['mcp-session-id'] : undefined;\n\n // Existing session: adopt the presented bearer (clients may have\n // refreshed their Zendesk token mid-session) and route the request to\n // the session's transport.\n if (sessionId) {\n const session = sessions.get(sessionId);\n if (session) {\n session.auth.bearer = bearer;\n session.lastActivityAt = Date.now();\n const body =\n req.method === 'POST'\n ? await readJsonBody(req, maxBodyBytes)\n : ({ ok: true, value: undefined } as const);\n if (!body.ok) {\n respondBodyError(req, res, body);\n return;\n }\n await session.transport.handleRequest(req, res, body.value);\n return;\n }\n }\n\n if (req.method !== 'POST') {\n // Non-POST without a session ID can't initialize a new session.\n sendJsonRpcError(res, 400, -32000, 'No active session; initialize via POST first.');\n return;\n }\n\n const body = await readJsonBody(req, maxBodyBytes);\n if (!body.ok) {\n respondBodyError(req, res, body);\n return;\n }\n\n // New session: the bearer is held in a per-session mutable cell read by\n // the per-session McpServer's token source — no async-local storage\n // needed because each session has its own server instance.\n const auth = { bearer };\n const server = createMcpServer(config, () => auth.bearer, logger);\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomUUID(),\n onsessioninitialized: (newId) => {\n sessions.set(newId, {\n transport,\n auth,\n lastActivityAt: Date.now(),\n close: async () => {\n await transport.close();\n await server.close();\n },\n });\n },\n });\n transport.onclose = () => {\n if (transport.sessionId) sessions.delete(transport.sessionId);\n };\n // The SDK's Transport interface uses `onclose?: () => void` which conflicts\n // with the concrete transport's class member typing under\n // exactOptionalPropertyTypes — the runtime contract is identical, this is\n // purely a type-system seam.\n // biome-ignore lint/suspicious/noExplicitAny: SDK type seam, see above\n await server.connect(transport as any);\n await transport.handleRequest(req, res, body.value);\n };\n\n const requestListener = async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n try {\n // CORS preflight is short-circuited before routing; for actual requests\n // we attach the allow-headers BEFORE we dispatch so they ride along\n // with the response regardless of which handler ends it.\n if (handleCorsPreflight(req, res, config.corsOrigins)) return;\n applyCorsHeaders(req, res, config.corsOrigins);\n\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);\n\n if (url.pathname === '/.well-known/oauth-protected-resource' && req.method === 'GET') {\n sendJson(res, 200, metadata.protectedResource);\n return;\n }\n if (url.pathname === '/.well-known/oauth-authorization-server' && req.method === 'GET') {\n sendJson(res, 200, metadata.authorizationServer);\n return;\n }\n if (url.pathname === '/healthz' && req.method === 'GET') {\n sendJson(res, 200, { status: 'ok', subdomain: config.subdomain });\n return;\n }\n if (url.pathname === '/mcp') {\n await handleMcpRequest(req, res);\n return;\n }\n\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Not found', path: url.pathname }));\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Internal Server Error';\n if (!res.headersSent) {\n sendJsonRpcError(res, 500, -32603, message);\n } else if (!res.writableEnded) {\n // Headers already on the wire: appending a JSON error mid-stream would\n // corrupt the body; just terminate the response.\n res.end();\n }\n }\n };\n\n const httpServer: Server = createServer((req, res) => {\n void requestListener(req, res);\n });\n\n await new Promise<void>((resolve, reject) => {\n httpServer.once('error', reject);\n httpServer.listen(config.port, config.host, () => {\n httpServer.off('error', reject);\n resolve();\n });\n });\n\n const addr = httpServer.address();\n const boundPort = typeof addr === 'object' && addr !== null ? addr.port : config.port;\n logger.info('http_transport_ready', { host: config.host, port: boundPort });\n\n const sweepIdleSessions = async (): Promise<void> => {\n const cutoff = Date.now() - idleTimeoutMs;\n for (const [id, session] of sessions) {\n if (session.lastActivityAt > cutoff) continue;\n sessions.delete(id);\n try {\n await session.close();\n } catch (err) {\n logger.warn('session_close_failed', {\n sessionId: id,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n };\n const sweeper = setInterval(\n () => void sweepIdleSessions(),\n options.sweepIntervalMs ?? SESSION_SWEEP_INTERVAL_MS,\n );\n // The sweeper must never keep the process alive on its own.\n sweeper.unref();\n\n return {\n port: boundPort,\n close: async () => {\n clearInterval(sweeper);\n await Promise.all([...sessions.values()].map((session) => session.close()));\n sessions.clear();\n // server.close() waits for every connection to drain; a wedged or\n // keep-alive socket would stall shutdown forever. Sessions are already\n // closed at this point, so dropping the remaining sockets is safe.\n httpServer.closeAllConnections();\n await new Promise<void>((resolve, reject) => {\n httpServer.close((err) => (err ? reject(err) : resolve()));\n });\n },\n };\n};\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { type Logger, silentLogger } from '../utils/logger';\n\nexport const startStdioTransport = async (\n server: McpServer,\n logger: Logger = silentLogger,\n): Promise<void> => {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n logger.info('stdio_transport_ready');\n};\n","import { buildBasicAuthHeader } from './auth/api-token';\nimport { createTokenStore } from './auth/token-store';\nimport type { Config } from './config';\nimport { loadConfig } from './config';\nimport { createMcpServer } from './server';\nimport { startHttpTransport } from './transports/http';\nimport { startStdioTransport } from './transports/stdio';\nimport { createLogger, type Logger } from './utils/logger';\n\nconst buildStdioServer = (config: Config, logger: Logger) => {\n if (config.zendeskEmail && config.zendeskApiToken) {\n // API token mode — static Basic auth. No onUnauthorized callback: a stale\n // API token is a credential rotation problem, not something the server\n // can recover from at runtime.\n const staticToken = buildBasicAuthHeader(config.zendeskEmail, config.zendeskApiToken);\n return createMcpServer(config, () => staticToken, logger);\n }\n // OAuth mode — browser-based auth on first tool call. `invalidate` drops the\n // dead access token on a 401 so the next call refreshes/re-authenticates.\n const tokenStore = createTokenStore(\n {\n subdomain: config.subdomain,\n oauthClientId: config.oauthClientId,\n callbackPort: config.callbackPort,\n },\n logger,\n );\n return createMcpServer(config, tokenStore.getToken, logger, tokenStore.invalidate);\n};\n\nconst main = async (): Promise<void> => {\n const config = loadConfig();\n const logger = createLogger(config.logLevel);\n\n if (config.transport === 'stdio') {\n const server = buildStdioServer(config, logger);\n await startStdioTransport(server, logger);\n return;\n }\n\n // HTTP mode: the HTTP transport creates a per-session McpServer with the\n // request's bearer captured in its tools' closure — no shared state.\n await startHttpTransport(config, logger);\n};\n\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fruggr/zendesk-mcp-server",
3
- "version": "1.9.0",
3
+ "version": "1.9.1",
4
4
  "description": "Model Context Protocol (MCP) server for the Zendesk Support & Help Center APIs — runs locally (stdio) or as a private remote MCP server (HTTP), with per-user OAuth 2.1 PKCE authentication, read-only mode, and token-efficient section-based article editing.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -65,7 +65,7 @@
65
65
  "engines": {
66
66
  "node": ">=20"
67
67
  },
68
- "packageManager": "pnpm@11.5.1+sha512.93f7b57422ea7068257235b4c16eb60762eb68e1dc23723199cc739043ea9be2c4143274a399d8c6defa2b1176226d9ca1c4b63482d6200c1a8fbaa78c1d1485",
68
+ "packageManager": "pnpm@11.5.2+sha512.71c631e382066efc25625d5cf029075de07b61b37f6e27350fbd84b1bda5864c8c1967adc280776b45c30a715c0359a3be08fef42d5bb09e2b99029979692916",
69
69
  "dependencies": {
70
70
  "@modelcontextprotocol/sdk": "1.29.0",
71
71
  "cheerio": "1.2.0",