mcpField
S
security-trust

security-trust

github.com/bartwaardenburgSecurity0176 installsUpdated 1w ago
Repository

About security-trust

A remediation skill for IsAgentReady.com’s Security & Trust checks. It guides site operators through correcting security headers, API CORS, WebMCP definitions, form behavior, and OAuth metadata, with verification commands.

Key features

  1. 01Fixes HTTPS and security headers
  2. 02Configures API CORS policy
  3. 03Checks WebMCP tool definitions
  4. 04Reviews risky form auto-submit
  5. 05Publishes OAuth metadata and auth guidance
  6. 06Provides read-only verification commands

Use cases

  1. 01Resolve failed Security & Trust scan checks
  2. 02Add missing headers to a web server
  3. 03Restrict API access by origin
  4. 04Prepare site authentication for agents

MCP support

Covers security checks for WebMCP tool definitions and agent authentication readiness; it is a remediation skill, not an MCP server.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/bartwaardenburg/isagentready-skills --skill security-trust

After installing, type/security-trustin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
checkpoints.md
gotchas.md
security-headers.md
server-configs.md

Details

From the project README

Security & Trust

Fixes the Security & Trust category of IsAgentReady.com. These checkpoints test the security baseline that agent protocols need, and the auth metadata that agents use to sign in.

When to Use

  • The scan shows failures in Security & Trust (legacy ids 5.x).
  • HTTPS, HSTS, CSP or other security headers are missing.
  • The public API needs a correct CORS policy.
  • WebMCP tool definitions contain secrets or auto-submit risky forms.
  • The site has a login and agents must find the OAuth metadata.

When NOT to Use

  • robots.txt or crawler access: use ai-content-discovery.
  • JSON-LD: use structured-data.
  • Semantic HTML: use content-semantics.
  • MCP, A2A or OpenAPI documents: use agent-protocols.

Instructions

  1. Get the failing checkpoints from the scan. Each result has a legacy id (5.3) and a stable id (ia.hsts).
  2. For the header checkpoints (5.1 to 5.9), use one server configuration from references/server-configs.md. It fixes all headers together.
  3. For the other checkpoints, go to the fix workflow with the anchor that is equal to the stable id.
  4. Replace example.com with the domain of the site. Run the verify command. All verify commands are read-only.
  5. Rescan with isagentready results <domain> --checkpoint <stable-id> or at isagentready.com.

If $ARGUMENTS is provided, use it as the URL to fix or as the checkpoint to fix.

Checkpoints

Strength is the agentready.org spec level (MUST, SHOULD, MAY). Strength does not change the score.

LegacyStable IDNameAR IDsStrength
5.1ia.httpsHTTPSnoneMUST
5.3ia.hstsHSTSnoneMUST
5.4ia.cspContent-Security-PolicynoneMUST
5.5ia.x-content-type-optionsX-Content-Type-OptionsnoneMUST
5.6ia.frame-protectionX-Frame-Options / frame-ancestorsnoneMUST
5.7ia.corsCORSnoneMAY
5.9ia.referrer-policyReferrer-PolicynoneMUST
5.10ia.tool-definition-securityTool definition securitynoneMAY
5.11ia.auto-submit-riskAuto-submit risknoneMAY
5.12ia.oauth-discoveryOAuth discoveryAR-ACT-01SHOULD
5.13ia.oauth-protected-resourceOAuth protected resourceAR-ACT-01SHOULD
5.14ia.agent-auth-readinessAgent auth readinessAR-ACT-01SHOULD

All Headers at Once

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; frame-ancestors 'none'" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Use always, so that Nginx also sends the headers on 3xx, 4xx and 5xx responses. Configurations for Apache, Caddy, Vercel, Netlify, Cloudflare, Express, Next.js and Django are in references/server-configs.md.

Verify all headers: curl -sfI https://example.com/ | grep -iE '^(strict-transport|content-security|x-content-type|x-frame|referrer-policy)'

Fix Workflows

5.1 HTTPS

Passes when the final URL after redirects uses https:// with a valid certificate.

  1. Get a certificate. With Let's Encrypt and Nginx: sudo certbot --nginx -d example.com -d www.example.com.
  2. Redirect HTTP to HTTPS:
    server { listen 80; server_name example.com; return 301 https://$host$request_uri; }
    

Verify: curl -sfI https://example.com/ | head -1

5.3 HSTS

Passes when Strict-Transport-Security has max-age of 31536000 or more. includeSubDomains is better. A short max-age gives partial credit.

  1. Add the header: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
  2. Add preload and submit the domain at hstspreload.org when all subdomains use HTTPS.

Verify: curl -sfI https://example.com/ | grep -i '^strict-transport-security'

5.4 Content-Security-Policy

Passes when the CSP has real directives. A CSP with only upgrade-insecure-requests gives partial credit.

  1. Start in report-only mode to find the sources that the site needs:
    add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' https://www.googletagmanager.com; report-uri /csp-report" always;
    
  2. Change to Content-Security-Policy when the reports are clean. See references/security-headers.md for nonces.

Verify: curl -sfI https://example.com/ | grep -i '^content-security-policy'

5.5 X-Content-Type-Options

Passes when the value is exactly nosniff.

  1. Add the header: add_header X-Content-Type-Options "nosniff" always;

Verify: curl -sfI https://example.com/ | grep -i '^x-content-type-options: nosniff'

5.6 X-Frame-Options / frame-ancestors

Passes when X-Frame-Options is DENY or SAMEORIGIN, or the CSP has frame-ancestors.

  1. Add frame-ancestors 'none' to the CSP, or add_header X-Frame-Options "DENY" always;.
  2. When trusted sites must embed the page, use frame-ancestors 'self' https://partner.example. Do not use ALLOW-FROM.

Verify: curl -sfI https://example.com/ | grep -iE '^x-frame-options|frame-ancestors'

5.7 CORS

Applies to the public API operation that 4.19 verifies. Passes when preflight and GET give matching Access-Control-Allow-* headers, no wildcard with credentials and Vary: Origin for a dynamic origin.

  1. Answer the preflight and the GET with the same policy:
    const allowed = new Set(["https://app.example.com", "https://agent.example"]);
    app.use("/api", (req, res, next) => {
      const origin = req.get("Origin");
      if (allowed.has(origin)) res.set({ "Access-Control-Allow-Origin": origin, Vary: "Origin" });
      res.set({ "Access-Control-Allow-Methods": "GET", "Access-Control-Allow-Headers": "Accept" });
      return req.method === "OPTIONS" ? res.sendStatus(204) : next();
    });
    
  2. For a public API without cookies, Access-Control-Allow-Origin: * is correct. Never combine * with Access-Control-Allow-Credentials: true.

Verify: curl -sI -H 'Origin: https://agent.example' https://example.com/api | grep -iE '^(access-control-|vary)'

5.9 Referrer-Policy

Passes when Referrer-Policy is present and is not unsafe-url.

  1. Add the header: add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Verify: curl -sfI https://example.com/ | grep -i '^referrer-policy'

5.10 Tool definition security

Applies when the page has WebMCP tool definitions. Passes when the definitions contain no secrets (JWT, API keys, tokens, card numbers, passwords), the names match the descriptions, and read-only tools have readOnlyHint.

  1. Remove each token or key from toolname, tooldescription and registerTool() code. Load secrets on the server.
  2. Mark tools that do not change data:
    document.modelContext?.registerTool({
      name: "search_products",
      description: "Search the product catalog by keyword",
      annotations: { readOnlyHint: true },
      inputSchema: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
      execute: async ({ query }) => searchProducts(query),
    });
    

Verify: curl -sf https://example.com/ | grep -ciE 'tool(name|description)="[^"]*(token|password|secret|api[_-]?key)' (expect 0)

5.11 Auto-submit risk

Applies when the page has forms. Passes when no form has toolautosubmit, or only search and filter forms have it. Risky actions give partial credit. Password or card fields with auto-submit fail.

  1. Remove toolautosubmit from login, payment, delete and send forms. Keep it on search forms:
    <form action="/search" toolname="search_products" tooldescription="Search the catalog" toolautosubmit>
      <input name="q" toolparamdescription="Search keywords">
    </form>
    

Verify: curl -sf https://example.com/ | grep -oE '<form[^>]*toolautosubmit[^>]*>' (expect only search or filter forms)

5.12 OAuth discovery

Applies when the site has auth signals. Passes when /.well-known/oauth-authorization-server or /.well-known/openid-configuration has issuer, authorization_endpoint and token_endpoint.

  1. Publish the metadata (RFC 8414):
    {
      "issuer": "https://example.com",
      "authorization_endpoint": "https://example.com/oauth/authorize",
      "token_endpoint": "https://example.com/oauth/token",
      "code_challenge_methods_supported": ["S256"],
      "scopes_supported": ["read", "write"]
    }
    

Verify: curl -sf https://example.com/.well-known/oauth-authorization-server | jq -e '.issuer and .authorization_endpoint and .token_endpoint'

5.13 OAuth protected resource

Applies when the site has auth signals. Passes when /.well-known/oauth-protected-resource has resource and authorization_servers, or the homepage sends WWW-Authenticate.

  1. Publish the metadata (RFC 9728):
    { "resource": "https://example.com/api", "authorization_servers": ["https://example.com"], "scopes_supported": ["read"] }
    
  2. On a 401 from the API, send WWW-Authenticate: Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource".

Verify: curl -sf https://example.com/.well-known/oauth-protected-resource | jq -e '.resource and (.authorization_servers | length > 0)'

5.14 Agent auth readiness

Applies when the site has an auth surface. The check combines PKCE S256, scopes metadata, protected resource metadata or a WWW-Authenticate header, a valid /auth.md and an agent-safe grant.

  1. Add "code_challenge_methods_supported": ["S256"] and scopes_supported to the metadata from 5.12.
  2. Publish 5.13.
  3. Write /auth.md:
    # Authentication
    
    - Flow: OAuth 2.1 authorization code with PKCE (S256)
    - Agents: use the device code grant or a user-approved token. Do not ask for passwords.
    - Scopes: read (default), write (needs user approval)
    

Verify: curl -sf https://example.com/.well-known/oauth-authorization-server | jq -e '.code_challenge_methods_supported | index("S256")'

Key Gotchas

  1. An HSTS max-age=86400 (one day) gives partial credit. Use one year.
  2. A CSP with only upgrade-insecure-requests gives partial credit.
  3. Nginx add_header without always skips error responses.
  4. Access-Control-Allow-Origin: * with credentials is invalid and unsafe.
  5. OAuth metadata on a different domain does not count for this site.

See references/gotchas.md for correct and incorrect examples.

References

Comments

Markdown supported