Skip to main content
Glama
badchars

osint-mcp-server

by badchars

dns_email_security

Analyze email security posture by checking SPF, DMARC, and DKIM records with risk assessment and recommendations for domains.

Instructions

Analyze email security posture: SPF, DMARC, DKIM records with risk assessment and recommendations. Checks common DKIM selectors (google, selector1, selector2, k1, etc.).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
domainYesDomain to analyze
dkim_selectorsNoCustom DKIM selectors to check (default: common selectors)

Implementation Reference

  • The handler function for dns_email_security that analyzes SPF, DMARC, and DKIM records to assess email security posture.
    export async function dnsEmailSecurity(domain: string, dkimSelectors?: string[]): Promise<EmailSecurityResult> {
      const selectors = dkimSelectors ?? DEFAULT_DKIM_SELECTORS;
      const recommendations: string[] = [];
    
      // SPF
      let spf: EmailSecurityResult["spf"] = { found: false, risk: "high" };
      try {
        const txts = await dns.resolveTxt(domain);
        const spfRecord = txts.map((t) => t.join("")).find((t) => t.startsWith("v=spf1"));
        if (spfRecord) {
          const includes = [...spfRecord.matchAll(/include:(\S+)/g)].map((m) => m[1]);
          let policy = "neutral";
          if (spfRecord.includes("-all")) policy = "hard_fail";
          else if (spfRecord.includes("~all")) policy = "soft_fail";
          else if (spfRecord.includes("+all")) policy = "pass_all";
          else if (spfRecord.includes("?all")) policy = "neutral";
    
          let risk = "low";
          if (policy === "pass_all") risk = "critical";
          else if (policy === "soft_fail") risk = "medium";
          else if (policy === "neutral") risk = "medium";
    
          if (policy === "soft_fail") recommendations.push("Upgrade SPF from ~all (soft fail) to -all (hard fail)");
          if (policy === "pass_all") recommendations.push("SPF +all allows any IP to send email — change to -all immediately");
    
          spf = { found: true, record: spfRecord, policy, includes, risk };
        } else {
          recommendations.push("No SPF record found — add v=spf1 with authorized senders");
          spf = { found: false, risk: "critical" };
        }
      } catch {
        spf = { found: false, risk: "high" };
      }
    
      // DMARC
      let dmarc: EmailSecurityResult["dmarc"] = { found: false, risk: "critical" };
      try {
        const txts = await dns.resolveTxt(`_dmarc.${domain}`);
        const dmarcRecord = txts.map((t) => t.join("")).find((t) => t.startsWith("v=DMARC1"));
        if (dmarcRecord) {
          const pMatch = dmarcRecord.match(/;\s*p=(\w+)/);
          const spMatch = dmarcRecord.match(/;\s*sp=(\w+)/);
          const ruaMatch = dmarcRecord.match(/;\s*rua=mailto:([^\s;]+)/);
          const policy = pMatch?.[1] ?? "none";
          const subdomainPolicy = spMatch?.[1];
          const rua = ruaMatch?.[1];
    
          let risk = "low";
          if (policy === "none") {
            risk = "high";
            recommendations.push("DMARC policy is p=none (monitoring only) — upgrade to p=quarantine then p=reject");
          } else if (policy === "quarantine") {
            risk = "medium";
          }
    
          dmarc = { found: true, record: dmarcRecord, policy, subdomain_policy: subdomainPolicy, rua, risk };
        } else {
          recommendations.push("No DMARC record found — add _dmarc TXT record with at least p=quarantine");
          dmarc = { found: false, risk: "critical" };
        }
      } catch {
        recommendations.push("No DMARC record found — add _dmarc TXT record with at least p=quarantine");
        dmarc = { found: false, risk: "critical" };
      }
    
      // DKIM
      const dkimResults: EmailSecurityResult["dkim"] = [];
      for (const selector of selectors) {
        try {
          const txts = await dns.resolveTxt(`${selector}._domainkey.${domain}`);
          const record = txts.map((t) => t.join("")).join("");
          let keySize: string | undefined;
          const pMatch = record.match(/p=([A-Za-z0-9+/=]+)/);
          if (pMatch) {
            const keyBytes = Math.ceil((pMatch[1].length * 3) / 4);
            const bits = keyBytes * 8;
            keySize = `${bits}-bit`;
            if (bits <= 1024) {
              recommendations.push(`DKIM selector "${selector}" uses ${bits}-bit key — upgrade to 2048-bit minimum`);
            }
          }
          dkimResults.push({ selector, found: true, record, keySize });
        } catch {
          // Also try CNAME (common for Microsoft 365 DKIM)
          try {
            const cnames = await dns.resolveCname(`${selector}._domainkey.${domain}`);
            dkimResults.push({ selector, found: true, record: `CNAME → ${cnames[0]}` });
          } catch {
            dkimResults.push({ selector, found: false });
          }
        }
      }
    
      if (!dkimResults.some((d) => d.found)) {
        recommendations.push("No DKIM records found for any common selector — configure DKIM signing");
      }
    
      // Overall risk
      const risks = [spf.risk, dmarc.risk];
      let overallRisk: EmailSecurityResult["overallRisk"] = "low";
      if (risks.includes("critical")) overallRisk = "critical";
      else if (risks.includes("high")) overallRisk = "high";
      else if (risks.includes("medium")) overallRisk = "medium";
    
      return { domain, spf, dmarc, dkim: dkimResults, overallRisk, recommendations };
    }
  • Tool definition and registration for dns_email_security in the protocol tools file.
    const dnsEmailSecurityTool: ToolDef = {
      name: "dns_email_security",
      description: "Analyze email security posture: SPF, DMARC, DKIM records with risk assessment and recommendations. Checks common DKIM selectors (google, selector1, selector2, k1, etc.).",
      schema: {
        domain: z.string().describe("Domain to analyze"),
        dkim_selectors: z.array(z.string()).optional().describe("Custom DKIM selectors to check (default: common selectors)"),
      },
      execute: async (args) =>
        json(await dnsEmailSecurity(args.domain as string, args.dkim_selectors as string[] | undefined)),
    };
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. While it mentions risk assessment and recommendations, it doesn't disclose important behavioral traits like whether this performs external DNS queries, has rate limits, requires authentication, returns structured vs. narrative output, or handles errors. The mention of 'common DKIM selectors' hints at default behavior but lacks detail.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately concise with two sentences that efficiently convey the core functionality. The first sentence states the primary purpose, and the second adds useful detail about DKIM selector checking. No wasted words, though it could be slightly more structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no annotations and no output schema that performs security analysis with 2 parameters, the description is incomplete. It doesn't explain what the risk assessment includes, what format recommendations take, whether this tool makes network calls, or what the output structure looks like. The agent lacks sufficient context to understand the full behavioral contract.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents both parameters well. The description adds minimal value by mentioning 'common DKIM selectors' which aligns with the dkim_selectors parameter description, but doesn't provide additional syntax, format details, or examples beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool analyzes email security posture by checking SPF, DMARC, and DKIM records with risk assessment and recommendations. It specifies the resource (email security posture) and verb (analyze), but doesn't explicitly differentiate from sibling tools like dns_lookup or dns_spf_chain that might perform related DNS queries.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With many sibling DNS and security tools available, there's no indication of when this comprehensive email security analysis is preferred over more specific tools like dns_spf_chain or whois_domain.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/badchars/osint-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server