Files
dobetternorge-tools/mcp-about.php
T
2026-06-01 07:12:48 +02:00

321 lines
14 KiB
PHP

<?php
declare(strict_types=1);
/**
* Public, indexable overview of the Do Better Norge MCP server.
* Unlike mcp.php (token mint, gated) and mcp-tool.php (per-tool detail), this is
* the marketing front door: anyone can read it; it links into /mcp.php to connect.
* Tool catalog is rendered live from DbnMcpRuntime::tools() so it never drifts.
*/
require_once __DIR__ . '/includes/bootstrap.php';
require_once __DIR__ . '/includes/DbnMcpRuntime.php';
$uiLang = dbnToolsCurrentLanguage();
$toolCatalog = DbnMcpRuntime::tools();
$toolCount = count($toolCatalog);
$endpointUrl = 'https://mcp.dobetternorge.no/mcp';
$healthUrl = 'https://mcp.dobetternorge.no/healthz';
$host = 'mcp.dobetternorge.no';
$toolIcons = [
'dbn.search_legal' => '🔍',
'dbn.corpus_search' => '🧭',
'dbn.corpus_stats' => '📊',
'dbn.list_documents' => '📚',
'dbn.get_document' => '📖',
'dbn.citation_graph' => '🔗',
'dbn.korrespond' => '✉️',
'dbn.korrespond_refine' => '✨',
'dbn.advocate_brief' => '🏛️',
'dbn.deep_research' => '🔬',
'dbn.ask' => '💬',
'dbn.summarize' => '📋',
'dbn.legal_analysis' => '⚖️',
'dbn.timeline' => '📅',
'dbn.barnevernet_analyze' => '📄',
'dbn.discrepancy_find' => '🔄',
'dbn.extract_text' => '📑',
'dbn.transcribe_audio' => '🎤',
'dbn.translate' => '🌍',
'dbn.redact' => '🔒',
'dbn.case_workbench_plan' => '🗂️',
'dbn.save_to_case' => '💾',
];
// Presentation groups (ordered). Any tool not listed falls into "More tools".
$toolGroups = [
'Search & retrieval' => ['dbn.search_legal', 'dbn.corpus_search', 'dbn.corpus_stats', 'dbn.list_documents', 'dbn.get_document', 'dbn.citation_graph'],
'Drafting & correspondence' => ['dbn.korrespond', 'dbn.korrespond_refine', 'dbn.advocate_brief', 'dbn.deep_research'],
'Analysis' => ['dbn.ask', 'dbn.summarize', 'dbn.legal_analysis', 'dbn.timeline', 'dbn.barnevernet_analyze', 'dbn.discrepancy_find'],
'Documents, media & privacy'=> ['dbn.extract_text', 'dbn.transcribe_audio', 'dbn.translate', 'dbn.redact'],
'Case workbench' => ['dbn.case_workbench_plan', 'dbn.save_to_case'],
];
// Index tools by slug for grouped rendering.
$bySlug = [];
foreach ($toolCatalog as $t) {
$bySlug[(string)($t['slug'] ?? '')] = $t;
}
$grouped = [];
$claimed = [];
foreach ($toolGroups as $groupName => $slugs) {
$bucket = [];
foreach ($slugs as $s) {
if (isset($bySlug[$s])) {
$bucket[] = $bySlug[$s];
$claimed[$s] = true;
}
}
if ($bucket) {
$grouped[$groupName] = $bucket;
}
}
$leftover = [];
foreach ($toolCatalog as $t) {
if (empty($claimed[(string)($t['slug'] ?? '')])) {
$leftover[] = $t;
}
}
if ($leftover) {
$grouped['More tools'] = $leftover;
}
?>
<!doctype html>
<html lang="<?= htmlspecialchars($uiLang) ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MCP server — Norwegian legal tools | Do Better Norge</title>
<meta name="description" content="The Do Better Norge legal toolkit as a Model Context Protocol (MCP) server. Connect Claude, n8n, Cursor or any MCP client to <?= (int)$toolCount ?> source-grounded Norwegian family-law and public-law tools.">
<link rel="canonical" href="https://tools.dobetternorge.no/mcp-about.php">
<meta property="og:title" content="Do Better Norge — Norwegian legal tools as an MCP server">
<meta property="og:description" content="<?= (int)$toolCount ?> source-grounded Norwegian family-law and public-law tools, callable from any MCP client.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://tools.dobetternorge.no/mcp-about.php">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Crimson+Pro:wght@400;600;700&family=IBM+Plex+Sans:wght@400;500;600;700&display=swap">
<link rel="stylesheet" href="assets/css/tools.css">
<link rel="stylesheet" href="assets/css/dbn-tools-redesign.css">
<style>
.mcp-hero {
padding: 2.75rem 1.5rem 2.25rem;
text-align: center;
border-bottom: 1px solid var(--dbn-line, rgba(22,19,15,.16));
margin-bottom: 2rem;
}
.mcp-hero__badge {
display: inline-flex;
align-items: center;
gap: 0.4rem;
background: #ede9fe;
color: #5b21b6;
font-size: 0.75rem;
font-weight: 600;
letter-spacing: .04em;
text-transform: uppercase;
padding: 0.3rem 0.75rem;
border-radius: 999px;
margin-bottom: 1rem;
}
.mcp-hero h1 {
font-family: 'Crimson Pro', serif;
font-size: clamp(1.8rem, 4vw, 2.6rem);
font-weight: 700;
color: var(--dbn-blue, #00205b);
margin: 0 0 0.7rem;
}
.mcp-hero p {
color: var(--muted, #667085);
font-size: 1.02rem;
max-width: 620px;
margin: 0 auto 1.4rem;
line-height: 1.6;
}
.mcp-hero__cta {
display: inline-flex;
align-items: center;
gap: 0.4rem;
background: var(--dbn-blue, #00205b);
color: #fff;
text-decoration: none;
font-weight: 600;
font-size: 0.92rem;
padding: 0.7rem 1.4rem;
border-radius: 8px;
transition: background .15s;
}
.mcp-hero__cta:hover { background: #001a4a; }
.mcp-conn {
border: 1px solid var(--dbn-line, rgba(22,19,15,.16));
border-radius: 12px;
background: #fff;
padding: 1.25rem 1.4rem;
margin-bottom: 2rem;
}
.mcp-conn__row {
display: flex;
gap: 1rem;
padding: 0.5rem 0;
border-bottom: 1px solid var(--dbn-line, #eef0f4);
font-size: 0.9rem;
}
.mcp-conn__row:last-child { border-bottom: 0; }
.mcp-conn__label { width: 130px; flex-shrink: 0; color: var(--muted, #667085); }
.mcp-conn__val {
font-family: 'IBM Plex Mono', 'Courier New', monospace;
font-size: 0.84rem;
color: var(--dbn-ink, #16130f);
word-break: break-all;
}
.mcp-group { margin-bottom: 1.75rem; }
.mcp-group__title {
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: .06em;
color: var(--muted, #667085);
font-weight: 600;
margin: 0 0 0.75rem;
}
.mcp-tool-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 0.75rem;
}
.mcp-tool-card {
display: flex;
gap: 0.7rem;
border: 1px solid var(--dbn-line, rgba(22,19,15,.16));
border-radius: 10px;
padding: 0.85rem 1rem;
background: #fff;
text-decoration: none;
transition: border-color .15s, box-shadow .15s;
}
.mcp-tool-card:hover {
border-color: var(--dbn-blue, #00205b);
box-shadow: 0 4px 12px rgba(0,32,91,.08);
}
.mcp-tool-card__icon { font-size: 1.25rem; line-height: 1.4; flex-shrink: 0; }
.mcp-tool-card__name { font-weight: 600; font-size: 0.88rem; color: var(--dbn-blue, #00205b); margin-bottom: 0.15rem; }
.mcp-tool-card__slug {
font-family: 'IBM Plex Mono', 'Courier New', monospace;
font-size: 0.71rem; color: var(--muted, #667085); margin-bottom: 0.3rem;
}
.mcp-tool-card__desc {
font-size: 0.82rem; color: var(--dbn-ink, #16130f); line-height: 1.45;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.mcp-privacy {
background: var(--dbn-paper, #f6f2ea);
border-radius: 8px;
padding: 1rem 1.25rem;
font-size: 0.85rem;
color: var(--dbn-ink, #16130f);
}
.mcp-privacy strong { color: var(--dbn-blue, #00205b); }
</style>
</head>
<body data-authenticated="<?= dbnToolsIsAuthenticated() ? 'true' : 'false' ?>" class="lt-app">
<script>
window.DBN_TOOLS_AUTHENTICATED = <?= dbnToolsIsAuthenticated() ? 'true' : 'false' ?>;
window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
</script>
<?php include __DIR__ . '/includes/nav.php'; ?>
<div class="account-shell">
<!-- ── Hero ─────────────────────────────────────────────────── -->
<div class="mcp-hero">
<div class="mcp-hero__badge">Model Context Protocol</div>
<h1>Norwegian family-law &amp; public-law tools, as an MCP server.</h1>
<p>
The Do Better Norge legal toolkit is exposed as a Model Context Protocol server. Any MCP
client — Claude, an n8n agent, Cursor or your own SDK — can search the legal corpus
(family law, child-welfare, procurement, health, labour, tax, ECHR case law and more),
draft correspondence to authorities, analyze Barnevernet documents and more, all grounded
in the live DBN legal sources.
</p>
<a class="mcp-hero__cta" href="/mcp.php">Mint a token &amp; connect →</a>
</div>
<!-- ── Connection ───────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title">Server endpoint</p>
<p style="color:var(--muted,#667085); font-size:0.88rem; margin-top:0;">
A live Streamable-HTTP bridge in front of the DBN tool runtime. Tools are registered
dynamically per session, so the catalog always reflects the server. Access is gated by a
member token (Plus/Pro), so each call runs in the token-holder's own tenant.
</p>
<div class="mcp-conn">
<div class="mcp-conn__row"><span class="mcp-conn__label">Host</span><span class="mcp-conn__val"><?= htmlspecialchars($host) ?></span></div>
<div class="mcp-conn__row"><span class="mcp-conn__label">Endpoint</span><span class="mcp-conn__val"><?= htmlspecialchars($endpointUrl) ?></span></div>
<div class="mcp-conn__row"><span class="mcp-conn__label">Transport</span><span class="mcp-conn__val">Streamable HTTP (MCP)</span></div>
<div class="mcp-conn__row"><span class="mcp-conn__label">Auth</span><span class="mcp-conn__val">Bearer token (Plus/Pro member)</span></div>
<div class="mcp-conn__row"><span class="mcp-conn__label">Health</span><span class="mcp-conn__val"><?= htmlspecialchars($healthUrl) ?></span></div>
<div class="mcp-conn__row"><span class="mcp-conn__label">Tools</span><span class="mcp-conn__val"><?= (int)$toolCount ?> registered</span></div>
</div>
</section>
<!-- ── Tool catalog ─────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title"><?= (int)$toolCount ?> tools available</p>
<p style="color:var(--muted,#667085); font-size:0.88rem; margin-top:0; margin-bottom:1.25rem;">
Every tool returns source-grounded output with citations where applicable. This is
informational legal assistance, not legal advice.
</p>
<?php foreach ($grouped as $groupName => $tools): ?>
<div class="mcp-group">
<p class="mcp-group__title"><?= htmlspecialchars($groupName) ?></p>
<div class="mcp-tool-grid">
<?php foreach ($tools as $tool):
$slug = (string)($tool['slug'] ?? '');
$name = (string)($tool['display_name'] ?? $slug);
$desc = (string)($tool['description'] ?? '');
$icon = $toolIcons[$slug] ?? '🔧';
$detailUrl = '/mcp-tool.php?tool=' . urlencode($slug) . ($uiLang !== 'en' ? '&lang=' . urlencode($uiLang) : '');
?>
<a class="mcp-tool-card" href="<?= htmlspecialchars($detailUrl) ?>">
<span class="mcp-tool-card__icon" aria-hidden="true"><?= $icon ?></span>
<span>
<span class="mcp-tool-card__name"><?= htmlspecialchars($name) ?></span>
<span class="mcp-tool-card__slug"><?= htmlspecialchars($slug) ?></span>
<span class="mcp-tool-card__desc"><?= htmlspecialchars($desc) ?></span>
</span>
</a>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
</section>
<!-- ── Connect ──────────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title">Connect from your client</p>
<p style="color:var(--muted,#667085); font-size:0.88rem; margin-top:0;">
Plus and Pro members mint a personal MCP token, then point Claude Desktop, Claude Code,
Cursor, VS Code or any Streamable-HTTP MCP client at the endpoint above. The server
registers all <?= (int)$toolCount ?> <code>dbn.*</code> tools per session.
</p>
<a href="/mcp.php" class="mcp-hero__cta" style="margin-top:0.5rem;">Open MCP setup →</a>
</section>
<!-- ── Privacy ──────────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title">Privacy</p>
<div class="mcp-privacy">
<strong>Default privacy is process-and-forget.</strong> Tool calls are not persisted to a
case record unless you explicitly call <code>dbn.save_to_case</code>.
</div>
</section>
</div><!-- /.account-shell -->
<?php require_once __DIR__ . '/includes/footer.php'; ?>
</body>
</html>