dns_block_domain
Block a domain on your DNS server to deny all queries to it. Specify the domain name to add it to the blocklist.
Instructions
Block a domain name. Queries to this domain will be denied by the DNS server.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| domain | Yes | Domain name to block (e.g. ads.example.com) |
Implementation Reference
- src/tools/blocking.ts:48-58 (handler)Handler function for dns_block_domain. Takes a 'domain' argument, validates it using validateDomain(), calls the Technitium API endpoint '/api/blocked/add' via client.callOrThrow(), and returns a JSON response with success status and the blocked domain.
handler: async (args) => { const domain = validateDomain(args.domain as string); const data = await client.callOrThrow("/api/blocked/add", { domain, }); return JSON.stringify( { success: true, blocked: domain, ...data }, null, 2 ); }, - src/tools/blocking.ts:36-45 (schema)Input schema for dns_block_domain. Defines a required 'domain' string parameter and provides a description. The tool is marked as not readonly (mutates state).
inputSchema: { type: "object", properties: { domain: { type: "string", description: "Domain name to block (e.g. ads.example.com)", }, }, required: ["domain"], }, - src/tools/index.ts:14-27 (registration)The getAllTools function aggregates all tool entries, including blockingTools(client) which contains dns_block_domain. This is called from the main server setup in src/index.ts.
export function getAllTools(client: TechnitiumClient): ToolEntry[] { return [ ...dashboardTools(client), ...dnsClientTools(client), ...zoneTools(client), ...recordTools(client), ...blockingTools(client), ...cacheTools(client), ...settingsTools(client), ...logTools(client), ...appTools(client), ...dnssecTools(client), ]; } - src/index.ts:18-148 (registration)Main server entry point that loads tools via getAllTools(), filters by readonly mode, and registers them with the MCP server via CallToolRequestSchema handler which dispatches to the tool's handler.
async function main(): Promise<void> { const config = loadConfig(); const client = new TechnitiumClient(config); const allTools = getAllTools(client); // Filter out write tools in readonly mode const tools = config.readonly ? allTools.filter((t) => t.readonly) : allTools; if (config.readonly) { audit.logSecurity( "readonly_mode", `Exposing ${tools.length} of ${allTools.length} tools (write tools hidden)` ); } const toolMap = new Map(tools.map((t) => [t.definition.name, t])); const rateLimiter = new RateLimiter(); const server = new Server( { name: "technitium-mcp", version: VERSION }, { capabilities: { tools: {} } } ); server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => t.definition), })); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const tool = toolMap.get(name); if (!tool) { return { content: [ { type: "text" as const, text: JSON.stringify({ error: `Unknown tool: ${name}` }) }, ], isError: true, }; } // Rate limit check const rateCheck = rateLimiter.check(name); if (!rateCheck.allowed) { audit.logSecurity("rate_limited", `Tool ${name} rate limited`); return { content: [ { type: "text" as const, text: JSON.stringify({ error: "Rate limited", retryAfterMs: rateCheck.retryAfterMs, }), }, ], isError: true, }; } const startTime = Date.now(); try { const rawResult = await tool.handler((args || {}) as Record<string, unknown>); // Sanitize the response let sanitized: string; try { const parsed = JSON.parse(rawResult); sanitized = JSON.stringify(sanitizeResponse(parsed), null, 2); } catch { sanitized = rawResult; } audit.logToolCall( name, (args || {}) as Record<string, unknown>, "success", Date.now() - startTime ); return { content: [{ type: "text" as const, text: sanitized }], }; } catch (error) { const rawMessage = error instanceof Error ? error.message : String(error); const message = sanitizeError(rawMessage); audit.logToolCall( name, (args || {}) as Record<string, unknown>, "error", Date.now() - startTime, message ); return { content: [ { type: "text" as const, text: JSON.stringify({ error: message }), }, ], isError: true, }; } }); let shuttingDown = false; const shutdown = async (signal: string) => { if (shuttingDown) return; shuttingDown = true; audit.logShutdown(signal); client.clearToken(); await server.close(); process.exit(0); }; process.on("SIGINT", () => shutdown("SIGINT")); process.on("SIGTERM", () => shutdown("SIGTERM")); const transport = new StdioServerTransport(); audit.logStartup(VERSION, maskUrl(config.url)); await server.connect(transport); } main().catch((error) => { const message = error instanceof Error ? error.message : String(error); audit.logSecurity("fatal_error", sanitizeError(message)); process.exit(1); }); - src/validate.ts:5-17 (helper)The validateDomain function used by the handler to sanitize and validate the domain name argument (trims, lowercases, checks length and regex pattern).
export function validateDomain(domain: string): string { if (!domain || typeof domain !== "string") { throw new Error("Domain name is required"); } const trimmed = domain.trim().toLowerCase(); if (trimmed.length > 253) { throw new Error("Domain name exceeds maximum length of 253 characters"); } if (!DOMAIN_RE.test(trimmed)) { throw new Error("Invalid domain name format"); } return trimmed; } - src/rate-limit.ts:32-40 (helper)Rate limiting configuration for dns_block_domain. It is grouped with other 'mutate' operations limited to 10 requests per 60-second window.
for (const tool of [ "dns_create_zone", "dns_add_record", "dns_update_record", "dns_block_domain", "dns_allow_domain", "dns_remove_allowed", "dns_remove_blocked", "dns_delete_cached", "dns_enable_zone", "dns_disable_zone", "dns_set_zone_options", "dns_set_settings", "dns_install_app", ]) { this.toolLimits.set(tool, mutateLimits); }