@capitalthought/agentsfirst-mcp 0.1.0 → 0.1.2

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/probe.d.ts CHANGED
@@ -168,6 +168,7 @@ export interface WebsiteSignals {
168
168
  ai_agents_addressed: string[];
169
169
  address_count: number;
170
170
  blanket_disallow: boolean;
171
+ content_signal_directives?: Record<string, string>;
171
172
  };
172
173
  markdown_negotiation: {
173
174
  requested?: string;
package/dist/probe.js CHANGED
@@ -344,6 +344,7 @@ export async function probeWebsite(url) {
344
344
  ['well_known_ai_plugin', `${origin}/.well-known/ai-plugin.json`],
345
345
  ['well_known_oauth', `${origin}/.well-known/oauth-authorization-server`],
346
346
  ['well_known_mcp', `${origin}/.well-known/mcp-server-card`],
347
+ ['well_known_mcp_json', `${origin}/.well-known/mcp-server-card.json`],
347
348
  ['sitemap', `${origin}/sitemap.xml`],
348
349
  ['openapi_root', `${origin}/openapi.json`],
349
350
  ['openapi_v1', `${origin}/v1/openapi.json`],
@@ -378,10 +379,26 @@ export async function probeWebsite(url) {
378
379
  'cohere-ai',
379
380
  ];
380
381
  const declared = aiAgents.filter((ua) => new RegExp(`User-agent:\\s*${ua}`, 'i').test(robotsBody));
382
+ // Content-Signal directive (Cloudflare's emerging AI-policy convention).
383
+ // Format: `Content-Signal: ai-train=yes, search=yes, ai-input=yes`
384
+ const contentSignalLines = robotsBody.match(/^Content-Signal:\s*(.+)$/gim) ?? [];
385
+ const directives = {};
386
+ for (const line of contentSignalLines) {
387
+ const m = line.match(/^Content-Signal:\s*(.+)$/i);
388
+ const captured = m?.[1];
389
+ if (!captured)
390
+ continue;
391
+ for (const pair of captured.split(',')) {
392
+ const [k, v] = pair.split('=').map((s) => s.trim().toLowerCase());
393
+ if (k && v)
394
+ directives[k] = v;
395
+ }
396
+ }
381
397
  out.signals.robots_analysis = {
382
398
  ai_agents_addressed: declared,
383
399
  address_count: declared.length,
384
400
  blanket_disallow: /User-agent:\s*\*[\s\S]*?Disallow:\s*\//i.test(robotsBody),
401
+ ...(Object.keys(directives).length > 0 && { content_signal_directives: directives }),
385
402
  };
386
403
  }
387
404
  const homepageBody = out.signals.homepage?.body;
package/dist/score.js CHANGED
@@ -488,22 +488,38 @@ function scoreBotAccessControl(signals) {
488
488
  let pts = 0;
489
489
  const notes = [];
490
490
  const robots = signals.signals.robots_analysis;
491
- if (robots) {
492
- if (robots.address_count > 0 && !robots.blanket_disallow) {
493
- pts += 10;
494
- notes.push('Per-bot allow/deny posture (no blanket disallow)');
495
- }
496
- else if (robots.address_count > 0) {
497
- pts += 5;
498
- notes.push('Addresses AI agents but uses blanket disallow');
499
- }
500
- if (robots.address_count >= 3) {
501
- pts += 5;
502
- notes.push(`Distinct posture for ${robots.address_count} bots — granular control`);
503
- }
491
+ if (!robots) {
492
+ notes.push('No robots.txt analysis available');
493
+ return { pts: 0, max, status: statusFor(0, max), notes };
494
+ }
495
+ // 10 pts: Cloudflare Content-Signal directive OR equivalent AI-policy declaration.
496
+ // Content-Signal is the modern machine-readable convention; per-bot rules are the
497
+ // older convention and earn partial credit when Content-Signal is absent.
498
+ const cs = robots.content_signal_directives;
499
+ if (cs && Object.keys(cs).length > 0) {
500
+ pts += 10;
501
+ const fmt = Object.entries(cs)
502
+ .map(([k, v]) => `${k}=${v}`)
503
+ .join(', ');
504
+ notes.push(`Content-Signal directive declared: ${fmt}`);
505
+ }
506
+ else if (robots.address_count >= 3) {
507
+ pts += 5;
508
+ notes.push(`AI policy implicit via ${robots.address_count} per-bot rules — consider adding a Content-Signal directive for machine-readable, future-proof AI policy`);
504
509
  }
505
510
  else {
506
- notes.push('No robots.txt analysis available');
511
+ notes.push('No Content-Signal directive and no per-bot AI policy in robots.txt');
512
+ }
513
+ // 5 pts: per-bot granular posture (names ≥3 AI bots and is not paired with a global blanket disallow).
514
+ if (robots.address_count >= 3 && !robots.blanket_disallow) {
515
+ pts += 5;
516
+ notes.push(`Per-bot granular posture for ${robots.address_count} named bots`);
517
+ }
518
+ else if (robots.address_count >= 3 && robots.blanket_disallow) {
519
+ notes.push(`Names ${robots.address_count} bots but pairs with a global blanket disallow`);
520
+ }
521
+ else if (robots.address_count > 0) {
522
+ notes.push(`Names ${robots.address_count} bot(s) — under threshold (need 3+) for granular-control credit`);
507
523
  }
508
524
  return { pts: Math.min(pts, max), max, status: statusFor(pts, max), notes };
509
525
  }
@@ -512,9 +528,12 @@ function scoreAgentCapabilities(signals) {
512
528
  let pts = 0;
513
529
  const notes = [];
514
530
  const surfaces = signals.signals.surfaces;
515
- if (surfaces['well_known_mcp']?.ok) {
531
+ if (surfaces['well_known_mcp']?.ok || surfaces['well_known_mcp_json']?.ok) {
516
532
  pts += 15;
517
- notes.push('/.well-known/mcp-server-card published');
533
+ const variant = surfaces['well_known_mcp_json']?.ok
534
+ ? '/.well-known/mcp-server-card.json'
535
+ : '/.well-known/mcp-server-card';
536
+ notes.push(`MCP Server Card published at ${variant}`);
518
537
  }
519
538
  else {
520
539
  notes.push('No MCP Server Card');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capitalthought/agentsfirst-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "MCP server that scores websites and codebases against the Agents First framework. Use it to check how agent-ready any product is. https://agentsfirst.dev",
5
5
  "license": "MIT",
6
6
  "author": "Joshua Baer <josh@capitalfactory.com>",