Localize mcp.php + add mcp-tool.php detail pages for all 19 MCP tools

- Replace all hardcoded English strings in mcp.php with dbnToolsT() calls
- Add 44 MCP UI chrome translation keys to includes/i18n.php (en/no/uk/pl)
- Generate includes/mcp-tool-translations.php with tool names, descriptions,
  and parameter docs translated into Norwegian, Ukrainian, and Polish via Azure OpenAI
- Create mcp-tool.php: parameterized detail page (?tool=dbn.slug) with parameter
  table, example request/response JSON, and privacy section, all localized
- Add "View details →" links on tool cards in mcp.php (shown on expand)
- Add translations/mcp-chrome.php and scripts/generate-mcp-translations.php

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 12:05:07 +02:00
parent e09ee62c62
commit 1bfafa9908
6 changed files with 2754 additions and 47 deletions
+200
View File
@@ -458,6 +458,56 @@ function dbnToolsTranslations(): array
'lt_engine_hint' => 'Engine: Azure GPT-4o · Legal documents are processed in memory and never stored.',
'lt_same_lang_error' => 'Source and target languages must be different.',
'lt_disclaimer' => 'This is an AI-assisted translation. Always verify with a qualified legal interpreter for official use.',
// MCP setup page + tool detail pages
'mcp_page_title' => 'MCP — Do Better Norge',
'mcp_meta_desc' => 'Connect Claude, Cursor, and other AI tools to all 19 DBN legal preparation tools via MCP.',
'mcp_hero_badge' => '✦ Plus & Pro',
'mcp_hero_h1' => 'Use DBN tools from Claude, Cursor & Copilot',
'mcp_hero_sub' => 'Connect any MCP client to all 19 Do Better Norge tools — transcription, legal analysis, timelines, redaction, and more.',
'mcp_token_section_title' => 'Your MCP token',
'mcp_gate_guest_p' => 'Sign in to create your personal MCP token. Available to Plus and Pro members.',
'mcp_gate_guest_btn' => 'Sign in',
'mcp_gate_free_p' => 'MCP access is available on Plus and Pro plans. Upgrade to connect your AI tools.',
'mcp_gate_free_btn' => 'Upgrade plan',
'mcp_token_hint' => 'Tokens are shown once at creation. Create one per client (Claude, Cursor, VS Code…).',
'mcp_token_create_btn' => 'Create token',
'mcp_token_reveal_label' => 'Copy this token now — it will not be shown again:',
'mcp_token_copy_btn' => 'Copy token',
'mcp_token_no_tokens' => 'No MCP tokens yet.',
'mcp_token_active' => 'Active',
'mcp_token_revoked' => 'Revoked',
'mcp_token_never_used' => 'Never used',
'mcp_token_last_used' => 'Last used',
'mcp_token_revoke_btn' => 'Revoke',
'mcp_config_title' => 'Client configuration',
'mcp_config_hint' => 'Paste your token into the config below after creating it above.',
'mcp_config_token_filled' => 'Token auto-filled.',
'mcp_config_run_terminal' => 'Run in your terminal:',
'mcp_test_btn' => 'Test connection',
'mcp_test_no_token' => 'Create a token first.',
'mcp_test_testing' => 'Testing…',
'mcp_tools_title' => 'Available tools',
'mcp_tools_sub' => 'All tools run on your Plus or Pro plan credits. Click a card for full technical details.',
'mcp_tools_param_req_hint' => 'Purple = required',
'mcp_tools_view_details' => 'View details →',
'mcp_privacy_title' => 'Privacy',
'mcp_privacy_text' => 'Process-and-forget by default. All tool calls process your text in memory and return results to your AI client. Nothing is saved to My Case unless you explicitly call dbn.save_to_case.',
'mcp_privacy_legal' => 'Tools provide legal preparation support, not final legal advice. Results are for informational purposes and should be reviewed by a qualified legal professional.',
'mcp_tool_back' => '← Back to MCP setup',
'mcp_tool_params_title' => 'Parameters',
'mcp_tool_no_params' => 'This tool takes no input parameters.',
'mcp_tool_col_param' => 'Parameter',
'mcp_tool_col_type' => 'Type',
'mcp_tool_col_required' => 'Required',
'mcp_tool_col_desc' => 'Description',
'mcp_tool_example_req' => 'Example request',
'mcp_tool_example_resp' => 'Example response',
'mcp_tool_connect_title' => 'Connect',
'mcp_tool_connect_text' => 'Create your MCP token on the setup page and use it with any supported client.',
'mcp_tool_setup_link' => 'Set up MCP →',
'mcp_tool_yes' => 'Yes',
'mcp_tool_no' => 'No',
],
'no' => [
'meta_title' => 'Do Better Norge - juridiske AI-verktøy',
@@ -848,6 +898,56 @@ function dbnToolsTranslations(): array
'lt_engine_hint' => 'Motor: Azure GPT-4o · Juridiske dokumenter behandles i minnet og lagres aldri.',
'lt_same_lang_error' => 'Kilde- og målspråk må være forskjellige.',
'lt_disclaimer' => 'Dette er en AI-assistert oversettelse. Verifiser alltid med en kvalifisert juridisk tolk til offisielt bruk.',
// MCP setup page + tool detail pages
'mcp_page_title' => 'MCP — Do Better Norge',
'mcp_meta_desc' => 'Koble Claude, Cursor og andre AI-verktøy til alle 19 DBN juridiske forberedelsesverktøy via MCP.',
'mcp_hero_badge' => '✦ Plus & Pro',
'mcp_hero_h1' => 'Bruk DBN-verktøy fra Claude, Cursor & Copilot',
'mcp_hero_sub' => 'Koble enhver MCP-klient til alle 19 Do Better Norge-verktøy — transkripsjon, juridisk analyse, tidslinjer, redigering og mer.',
'mcp_token_section_title' => 'Din MCP-token',
'mcp_gate_guest_p' => 'Logg inn for å opprette din personlige MCP-token. Tilgjengelig for Plus- og Pro-medlemmer.',
'mcp_gate_guest_btn' => 'Logg inn',
'mcp_gate_free_p' => 'MCP-tilgang er tilgjengelig på Plus- og Pro-planer. Oppgrader for å koble dine AI-verktøy.',
'mcp_gate_free_btn' => 'Oppgrader plan',
'mcp_token_hint' => 'Tokens vises én gang ved opprettelse. Opprett én per klient (Claude, Cursor, VS Code…).',
'mcp_token_create_btn' => 'Opprett token',
'mcp_token_reveal_label' => 'Kopier denne token nå — den vil ikke bli vist igjen:',
'mcp_token_copy_btn' => 'Kopier token',
'mcp_token_no_tokens' => 'Ingen MCP-tokens ennå.',
'mcp_token_active' => 'Aktiv',
'mcp_token_revoked' => 'Tilbaketrukket',
'mcp_token_never_used' => 'Aldri brukt',
'mcp_token_last_used' => 'Sist brukt',
'mcp_token_revoke_btn' => 'Tilbakekall',
'mcp_config_title' => 'Klientkonfigurasjon',
'mcp_config_hint' => 'Lim inn tokenet ditt i konfigurasjonen nedenfor etter å ha opprettet det ovenfor.',
'mcp_config_token_filled' => 'Token automatisk fylt ut.',
'mcp_config_run_terminal' => 'Kjør i terminalen din:',
'mcp_test_btn' => 'Test tilkobling',
'mcp_test_no_token' => 'Opprett et token først.',
'mcp_test_testing' => 'Tester…',
'mcp_tools_title' => 'Tilgjengelige verktøy',
'mcp_tools_sub' => 'Alle verktøy kjører på kreditter fra din Plus- eller Pro-plan. Klikk på et kort for full teknisk informasjon.',
'mcp_tools_param_req_hint' => 'Lilla = påkrevd',
'mcp_tools_view_details' => 'Se detaljer →',
'mcp_privacy_title' => 'Personvern',
'mcp_privacy_text' => 'Prosesser og glem som standard. Alle verktøyanrop prosesserer teksten din i minnet og returnerer resultater til din AI-klient. Ingenting lagres i Min sak med mindre du eksplisitt kaller dbn.save_to_case.',
'mcp_privacy_legal' => 'Verktøyene gir støtte til juridisk forberedelse, ikke endelig juridisk rådgivning. Resultatene er kun til informasjonsformål og bør vurderes av en kvalifisert juridisk profesjonell.',
'mcp_tool_back' => '← Tilbake til MCP-oppsett',
'mcp_tool_params_title' => 'Parametere',
'mcp_tool_no_params' => 'Dette verktøyet tar ingen inndata-parametere.',
'mcp_tool_col_param' => 'Parameter',
'mcp_tool_col_type' => 'Type',
'mcp_tool_col_required' => 'Påkrevd',
'mcp_tool_col_desc' => 'Beskrivelse',
'mcp_tool_example_req' => 'Eksempel på forespørsel',
'mcp_tool_example_resp' => 'Eksempel på svar',
'mcp_tool_connect_title' => 'Koble til',
'mcp_tool_connect_text' => 'Opprett din MCP-token på oppsettssiden og bruk den med enhver støttet klient.',
'mcp_tool_setup_link' => 'Sett opp MCP →',
'mcp_tool_yes' => 'Ja',
'mcp_tool_no' => 'Nei',
],
'uk' => [
'meta_title' => 'Do Better Norge - юридичні AI інструменти',
@@ -1238,6 +1338,56 @@ function dbnToolsTranslations(): array
'lt_engine_hint' => 'Механізм: Azure GPT-4o · Юридичні документи обробляються в пам\'яті та не зберігаються.',
'lt_same_lang_error' => 'Мова оригіналу та мова перекладу повинні бути різними.',
'lt_disclaimer' => 'Це переклад за допомогою штучного інтелекту. Завжди перевіряйте з кваліфікованим юридичним перекладачем для офіційного використання.',
// MCP setup page + tool detail pages
'mcp_page_title' => 'MCP — Do Better Norge',
'mcp_meta_desc' => 'Підключіть Claude, Cursor та інші інструменти ШІ до всіх 19 юридичних підготовчих інструментів DBN через MCP.',
'mcp_hero_badge' => '✦ Плюс та Профі',
'mcp_hero_h1' => 'Використовуйте інструменти DBN від Claude, Cursor та Copilot',
'mcp_hero_sub' => 'Підключіть будь-якого клієнта MCP до всіх 19 інструментів Do Better Norge — транскрипція, юридичний аналіз, часові лінії, редагування та інше.',
'mcp_token_section_title' => 'Ваш токен MCP',
'mcp_gate_guest_p' => 'Увійдіть, щоб створити свій особистий токен MCP. Доступно для учасників Плюс та Профі.',
'mcp_gate_guest_btn' => 'Увійти',
'mcp_gate_free_p' => 'Доступ до MCP доступний у планах Плюс та Профі. Оновіть, щоб підключити свої інструменти ШІ.',
'mcp_gate_free_btn' => 'Оновити план',
'mcp_token_hint' => 'Токени показуються один раз під час створення. Створіть один для кожного клієнта (Claude, Cursor, VS Code…).',
'mcp_token_create_btn' => 'Створити токен',
'mcp_token_reveal_label' => 'Скопіюйте цей токен зараз — він більше не буде показаний:',
'mcp_token_copy_btn' => 'Скопіювати токен',
'mcp_token_no_tokens' => 'Ще немає токенів MCP.',
'mcp_token_active' => 'Активний',
'mcp_token_revoked' => 'Скасовано',
'mcp_token_never_used' => 'Ніколи не використовувався',
'mcp_token_last_used' => 'Останній раз використовувався',
'mcp_token_revoke_btn' => 'Скасувати',
'mcp_config_title' => 'Конфігурація клієнта',
'mcp_config_hint' => 'Вставте свій токен у конфігурацію нижче після його створення вище.',
'mcp_config_token_filled' => 'Токен автоматично заповнений.',
'mcp_config_run_terminal' => 'Запустіть у вашому терміналі:',
'mcp_test_btn' => 'Перевірити з\'єднання',
'mcp_test_no_token' => 'Спочатку створіть токен.',
'mcp_test_testing' => 'Тестування…',
'mcp_tools_title' => 'Доступні інструменти',
'mcp_tools_sub' => 'Усі інструменти працюють на кредитах вашого плану Plus або Pro. Натисніть на картку для отримання повних технічних деталей.',
'mcp_tools_param_req_hint' => 'Фіолетовий = обов\'язковий',
'mcp_tools_view_details' => 'Переглянути деталі →',
'mcp_privacy_title' => 'Конфіденційність',
'mcp_privacy_text' => 'Обробка та забуття за замовчуванням. Усі виклики інструментів обробляють ваш текст у пам\'яті та повертають результати вашому AI-клієнту. Нічого не зберігається у My Case, якщо ви явно не викликаєте dbn.save_to_case.',
'mcp_privacy_legal' => 'Інструменти надають підтримку в підготовці юридичних документів, а не остаточні юридичні поради. Результати призначені лише для інформаційних цілей і повинні бути переглянуті кваліфікованим юридичним фахівцем.',
'mcp_tool_back' => '← Повернутися до налаштування MCP',
'mcp_tool_params_title' => 'Параметри',
'mcp_tool_no_params' => 'Цей інструмент не приймає вхідних параметрів.',
'mcp_tool_col_param' => 'Параметр',
'mcp_tool_col_type' => 'Тип',
'mcp_tool_col_required' => 'Обов\'язковий',
'mcp_tool_col_desc' => 'Опис',
'mcp_tool_example_req' => 'Приклад запиту',
'mcp_tool_example_resp' => 'Приклад відповіді',
'mcp_tool_connect_title' => 'Підключити',
'mcp_tool_connect_text' => 'Створіть свій токен MCP на сторінці налаштувань і використовуйте його з будь-яким підтримуваним клієнтом.',
'mcp_tool_setup_link' => 'Налаштувати MCP →',
'mcp_tool_yes' => 'Так',
'mcp_tool_no' => 'Ні',
],
'pl' => [
'meta_title' => 'Do Better Norge - prawne narzędzia AI',
@@ -1628,6 +1778,56 @@ function dbnToolsTranslations(): array
'lt_engine_hint' => 'Silnik: Azure GPT-4o · Dokumenty prawne są przetwarzane w pamięci i nigdy nie zapisywane.',
'lt_same_lang_error' => 'Języki źródłowy i docelowy muszą być różne.',
'lt_disclaimer' => 'To jest tłumaczenie wspomagane AI. Zawsze weryfikuj z wykwalifikowanym tłumaczem prawnym do oficjalnego użytku.',
// MCP setup page + tool detail pages
'mcp_page_title' => 'MCP — Do Better Norge',
'mcp_meta_desc' => 'Połącz Claude, Cursor i inne narzędzia AI z wszystkimi 19 narzędziami przygotowania prawnego DBN za pośrednictwem MCP.',
'mcp_hero_badge' => '✦ Plus & Pro',
'mcp_hero_h1' => 'Użyj narzędzi DBN z Claude, Cursor i Copilot',
'mcp_hero_sub' => 'Połącz dowolnego klienta MCP ze wszystkimi 19 narzędziami Do Better Norge — transkrypcja, analiza prawna, harmonogramy, redakcja i inne.',
'mcp_token_section_title' => 'Twój token MCP',
'mcp_gate_guest_p' => 'Zaloguj się, aby utworzyć swój osobisty token MCP. Dostępny dla członków Plus i Pro.',
'mcp_gate_guest_btn' => 'Zaloguj się',
'mcp_gate_free_p' => 'Dostęp do MCP jest dostępny w planach Plus i Pro. Uaktualnij, aby połączyć swoje narzędzia AI.',
'mcp_gate_free_btn' => 'Uaktualnij plan',
'mcp_token_hint' => 'Tokeny są wyświetlane tylko raz przy tworzeniu. Utwórz jeden na klienta (Claude, Cursor, VS Code…).',
'mcp_token_create_btn' => 'Utwórz token',
'mcp_token_reveal_label' => 'Skopiuj ten token teraz — nie będzie już wyświetlany:',
'mcp_token_copy_btn' => 'Skopiuj token',
'mcp_token_no_tokens' => 'Brak tokenów MCP.',
'mcp_token_active' => 'Aktywny',
'mcp_token_revoked' => 'Cofnięty',
'mcp_token_never_used' => 'Nigdy nie używany',
'mcp_token_last_used' => 'Ostatnio używany',
'mcp_token_revoke_btn' => 'Cofnij',
'mcp_config_title' => 'Konfiguracja klienta',
'mcp_config_hint' => 'Wklej swój token do konfiguracji poniżej po jego utworzeniu powyżej.',
'mcp_config_token_filled' => 'Token automatycznie wypełniony.',
'mcp_config_run_terminal' => 'Uruchom w swoim terminalu:',
'mcp_test_btn' => 'Testuj połączenie',
'mcp_test_no_token' => 'Najpierw utwórz token.',
'mcp_test_testing' => 'Testowanie…',
'mcp_tools_title' => 'Dostępne narzędzia',
'mcp_tools_sub' => 'Wszystkie narzędzia działają na kredytach Twojego planu Plus lub Pro. Kliknij kartę, aby uzyskać pełne szczegóły techniczne.',
'mcp_tools_param_req_hint' => 'Fioletowy = wymagany',
'mcp_tools_view_details' => 'Zobacz szczegóły →',
'mcp_privacy_title' => 'Prywatność',
'mcp_privacy_text' => 'Domyślnie przetwarzaj i zapomnij. Wszystkie wywołania narzędzi przetwarzają Twój tekst w pamięci i zwracają wyniki do Twojego klienta AI. Nic nie jest zapisywane w Mojej Sprawie, chyba że wyraźnie wywołasz dbn.save_to_case.',
'mcp_privacy_legal' => 'Narzędzia zapewniają wsparcie w zakresie przygotowania prawnego, a nie ostatecznej porady prawnej. Wyniki mają charakter informacyjny i powinny być przeglądane przez wykwalifikowanego profesjonalistę prawnego.',
'mcp_tool_back' => '← Powrót do konfiguracji MCP',
'mcp_tool_params_title' => 'Parametry',
'mcp_tool_no_params' => 'To narzędzie nie przyjmuje parametrów wejściowych.',
'mcp_tool_col_param' => 'Parametr',
'mcp_tool_col_type' => 'Typ',
'mcp_tool_col_required' => 'Wymagany',
'mcp_tool_col_desc' => 'Opis',
'mcp_tool_example_req' => 'Przykładowe żądanie',
'mcp_tool_example_resp' => 'Przykładowa odpowiedź',
'mcp_tool_connect_title' => 'Połącz',
'mcp_tool_connect_text' => 'Utwórz swój token MCP na stronie konfiguracji i użyj go z dowolnym obsługiwanym klientem.',
'mcp_tool_setup_link' => 'Skonfiguruj MCP →',
'mcp_tool_yes' => 'Tak',
'mcp_tool_no' => 'Nie',
],
];
}
+970
View File
@@ -0,0 +1,970 @@
<?php
// MCP tool translations — display_name, description, param descriptions
// DO NOT EDIT MANUALLY — regenerate with scripts/generate-mcp-translations.php
return array (
'dbn.search_legal' =>
array (
'en' =>
array (
'display_name' => 'Search DBN legal corpus',
'description' => 'Search the DBN Norwegian family-law corpus.',
'params' =>
array (
'query' => 'The search query (minimum 3 characters). Enter a legal topic, keyword, or question.',
'language' => 'Response language: en, no, uk, pl, or auto (detect from input).',
'limit' => 'Maximum number of results to return (110).',
'corpus_scope' => 'Which corpus to search: shared (public legal corpus), private (your uploaded documents), or both.',
),
),
'no' =>
array (
'display_name' => 'Søk i DBN juridisk korpus',
'description' => 'Søk i DBN norsk familierett korpus.',
'params' =>
array (
'query' => 'Søketeksten (minimum 3 tegn). Skriv inn et juridisk emne, nøkkelord eller spørsmål.',
'language' => 'Svarspråk: en, no, uk, pl, eller auto (oppdages fra input).',
'limit' => 'Maksimalt antall resultater som skal returneres (110).',
'corpus_scope' => 'Hvilket korpus skal søkes i: delt (offentlig juridisk korpus), privat (dine opplastede dokumenter), eller begge.',
),
),
'uk' =>
array (
'display_name' => 'Пошук юридичного корпусу DBN',
'description' => 'Пошук норвезького корпусу сімейного права DBN.',
'params' =>
array (
'query' => 'Пошуковий запит (мінімум 3 символи). Введіть юридичну тему, ключове слово або запитання.',
'language' => 'Мова відповіді: en, no, uk, pl або auto (виявити з введення).',
'limit' => 'Максимальна кількість результатів для повернення (1–10).',
'corpus_scope' => 'Який корпус шукати: спільний (публічний юридичний корпус), приватний (ваші завантажені документи) або обидва.',
),
),
'pl' =>
array (
'display_name' => 'Szukaj w korpusie prawnym DBN',
'description' => 'Szukaj w norweskim korpusie prawa rodzinnego DBN.',
'params' =>
array (
'query' => 'Zapytanie wyszukiwania (minimum 3 znaki). Wprowadź temat prawny, słowo kluczowe lub pytanie.',
'language' => 'Język odpowiedzi: en, no, uk, pl lub auto (wykrywanie na podstawie wejścia).',
'limit' => 'Maksymalna liczba wyników do zwrócenia (110).',
'corpus_scope' => 'Który korpus przeszukać: wspólny (publiczny korpus prawny), prywatny (twoje przesłane dokumenty) lub oba.',
),
),
),
'dbn.ask' =>
array (
'en' =>
array (
'display_name' => 'Ask a legal question',
'description' => 'Answer a legal preparation question with source-grounded DBN context.',
'params' =>
array (
'question' => 'The legal question to answer (minimum 5 characters).',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
),
),
'no' =>
array (
'display_name' => 'Still et juridisk spørsmål',
'description' => 'Svar på et juridisk forberedelsesspørsmål med kildebasert DBN kontekst.',
'params' =>
array (
'question' => 'Det juridiske spørsmålet som skal besvares (minimum 5 tegn).',
'language' => 'Svarspråk: en, no, uk, pl, eller auto.',
'use_case_context' => 'Inkluder kontekst fra din Case Workbench økt (true/false).',
),
),
'uk' =>
array (
'display_name' => 'Задати юридичне питання',
'description' => 'Відповісти на питання підготовки до суду з контекстом, основаним на DBN.',
'params' =>
array (
'question' => 'Юридичне питання для відповіді (мінімум 5 символів).',
'language' => 'Мова відповіді: en, no, uk, pl або auto.',
'use_case_context' => 'Включити контекст з вашої сесії Case Workbench (true/false).',
),
),
'pl' =>
array (
'display_name' => 'Zadaj pytanie prawne',
'description' => 'Odpowiedz na pytanie dotyczące przygotowania prawnego z kontekstem opartym na źródłach DBN.',
'params' =>
array (
'question' => 'Pytanie prawne do odpowiedzi (minimum 5 znaków).',
'language' => 'Język odpowiedzi: en, no, uk, pl lub auto.',
'use_case_context' => 'Uwzględnij kontekst z sesji Case Workbench (prawda/fałsz).',
),
),
),
'dbn.summarize' =>
array (
'en' =>
array (
'display_name' => 'Summarize document',
'description' => 'Summarize pasted case text with optional legal-corpus enrichment.',
'params' =>
array (
'text' => 'The text to summarize.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_legal_corpus' => 'Enrich the summary with relevant legal corpus passages (true/false).',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
),
),
'no' =>
array (
'display_name' => 'Oppsummer dokument',
'description' => 'Oppsummer innlimt sakstekst med valgfri berikelse fra juridisk korpus.',
'params' =>
array (
'text' => 'Teksten som skal oppsummeres.',
'language' => 'Svarspråk: en, no, uk, pl, eller auto.',
'use_legal_corpus' => 'Berik oppsummeringen med relevante passasjer fra juridisk korpus (true/false).',
'use_case_context' => 'Inkluder kontekst fra din Case Workbench økt (true/false).',
),
),
'uk' =>
array (
'display_name' => 'Підсумувати документ',
'description' => 'Підсумувати вставлений текст справи з можливим збагаченням юридичним корпусом.',
'params' =>
array (
'text' => 'Текст для підсумування.',
'language' => 'Мова відповіді: en, no, uk, pl або auto.',
'use_legal_corpus' => 'Збагачувати підсумок відповідними уривками з юридичного корпусу (true/false).',
'use_case_context' => 'Включити контекст з вашої сесії Case Workbench (true/false).',
),
),
'pl' =>
array (
'display_name' => 'Podsumuj dokument',
'description' => 'Podsumuj wklejony tekst sprawy z opcjonalnym wzbogaceniem korpusu prawnego.',
'params' =>
array (
'text' => 'Tekst do podsumowania.',
'language' => 'Język odpowiedzi: en, no, uk, pl lub auto.',
'use_legal_corpus' => 'Wzbogac podsumowanie o odpowiednie fragmenty korpusu prawnego (prawda/fałsz).',
'use_case_context' => 'Uwzględnij kontekst z sesji Case Workbench (prawda/fałsz).',
),
),
),
'dbn.timeline' =>
array (
'en' =>
array (
'display_name' => 'Extract timeline',
'description' => 'Extract dates, hearings, milestones, and deadlines from case text.',
'params' =>
array (
'text' => 'The case text to extract dates from.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'focus' => 'What to extract: all (every date), deadlines (appeal windows and filing deadlines), hearings (tribunal and court dates), cps (Barnevernet milestones).',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
),
),
'no' =>
array (
'display_name' => 'Ekstraher tidslinje',
'description' => 'Ekstraher datoer, høringer, milepæler og frister fra sakstekst.',
'params' =>
array (
'text' => 'Saksteksten for å ekstrahere datoer fra.',
'language' => 'Svarspråk: en, no, uk, pl, eller auto.',
'focus' => 'Hva som skal hentes ut: alle (hver dato), frister (klagefrister og innleveringsfrister), høringer (tribunal- og rettsdatoer), cps (Barnevernet milepæler).',
'use_case_context' => 'Inkluder kontekst fra din Case Workbench-økt (sann/usann).',
),
),
'uk' =>
array (
'display_name' => 'Витягти хронологію',
'description' => 'Витягти дати, слухання, етапи та терміни з тексту справи.',
'params' =>
array (
'text' => 'Текст справи для витягування дат.',
'language' => 'Мова відповіді: en, no, uk, pl або auto.',
'focus' => 'Що витягнути: всі (кожну дату), терміни (вікна апеляції та терміни подання), слухання (дати трибуналу та суду), cps (етапи Barnevernet).',
'use_case_context' => 'Включити контекст з вашої сесії Case Workbench (так/ні).',
),
),
'pl' =>
array (
'display_name' => 'Wyodrębnij oś czasu',
'description' => 'Wyodrębnij daty, przesłuchania, kamienie milowe i terminy z tekstu sprawy.',
'params' =>
array (
'text' => 'Tekst sprawy, z którego należy wyodrębnić daty.',
'language' => 'Język odpowiedzi: en, no, uk, pl lub auto.',
'focus' => 'Co wyodrębnić: wszystkie (wszystkie daty), terminy (okna odwoławcze i terminy składania), rozprawy (daty trybunałów i sądów), cps (kamienie milowe Barnevernet).',
'use_case_context' => 'Uwzględnij kontekst z sesji Case Workbench (prawda/fałsz).',
),
),
),
'dbn.redact' =>
array (
'en' =>
array (
'display_name' => 'Redact private data',
'description' => 'Remove or pseudonymize names, IDs, phone numbers, addresses, and places.',
'params' =>
array (
'text' => 'The text to redact.',
'language' => 'Language of the input text: en, no, uk, pl, or auto.',
'mode' => 'Redaction scope: standard (names, IDs, phones) or strict (also addresses, locations, institutions).',
'output_format' => 'Replacement style: contextual ([PERSON A]), generic (█████), or pseudonym (consistent invented names).',
),
),
'no' =>
array (
'display_name' => 'Rediger private data',
'description' => 'Fjern eller pseudonymiser navn, ID-er, telefonnumre, adresser og steder.',
'params' =>
array (
'text' => 'Teksten som skal redigeres.',
'language' => 'Språk for innholdsteksten: en, no, uk, pl, eller auto.',
'mode' => 'Redigeringsomfang: standard (navn, ID-er, telefoner) eller strengt (også adresser, steder, institusjoner).',
'output_format' => 'Erstatningsstil: kontekstuell ([PERSON A]), generell (█████), eller pseudonym (konsistente oppdiktede navn).',
),
),
'uk' =>
array (
'display_name' => 'Редагувати приватні дані',
'description' => 'Видалити або псевдонімізувати імена, ідентифікаційні номери, номери телефонів, адреси та місця.',
'params' =>
array (
'text' => 'Текст для редагування.',
'language' => 'Мова вхідного тексту: en, no, uk, pl або auto.',
'mode' => 'Обсяг редагування: стандартний (імена, ідентифікаційні номери, телефони) або строгий (також адреси, місця, установи).',
'output_format' => 'Стиль заміни: контекстуальний ([PERSON A]), загальний (█████) або псевдонім (послідовно вигадані імена).',
),
),
'pl' =>
array (
'display_name' => 'Redaguj dane prywatne',
'description' => 'Usuń lub zanonimizuj imiona, identyfikatory, numery telefonów, adresy i miejsca.',
'params' =>
array (
'text' => 'Tekst do redakcji.',
'language' => 'Język tekstu wejściowego: en, no, uk, pl lub auto.',
'mode' => 'Zakres redakcji: standardowy (imiona, identyfikatory, telefony) lub ścisły (także adresy, lokalizacje, instytucje).',
'output_format' => 'Styl zastąpienia: kontekstowy ([OSOBA A]), ogólny (█████) lub pseudonim (spójne wymyślone imiona).',
),
),
),
'dbn.translate' =>
array (
'en' =>
array (
'display_name' => 'Translate legal document',
'description' => 'Translate Norwegian family-law text with legal terminology annotations.',
'params' =>
array (
'text' => 'The text to translate.',
'source_lang' => 'Language of the input text. Use auto to detect automatically.',
'target_lang' => 'Language to translate into (required): en, no, uk, or pl.',
'doc_type' => 'Document type hint for legal terminology: auto, barnevernet, adopsjon, emergency, samvaer, fylkesnemnd, or other.',
),
),
'no' =>
array (
'display_name' => 'Oversett juridisk dokument',
'description' => 'Oversett norsk familierettstekst med juridiske terminologianotasjoner.',
'params' =>
array (
'text' => 'Teksten som skal oversettes.',
'source_lang' => 'Språk for innholdsteksten. Bruk auto for automatisk deteksjon.',
'target_lang' => 'Språk å oversette til (påkrevd): en, no, uk, eller pl.',
'doc_type' => 'Dokumenttypehint for juridisk terminologi: auto, barnevernet, adopsjon, akutt, samvær, fylkesnemnd, eller annet.',
),
),
'uk' =>
array (
'display_name' => 'Перекласти юридичний документ',
'description' => 'Перекласти текст норвезького сімейного права з анотаціями юридичної термінології.',
'params' =>
array (
'text' => 'Текст для перекладу.',
'source_lang' => 'Мова вхідного тексту. Використовуйте auto для автоматичного визначення.',
'target_lang' => 'Мова для перекладу (обов\'язково): en, no, uk або pl.',
'doc_type' => 'Підказка щодо типу документа для юридичної термінології: auto, barnevernet, adopsjon, emergency, samvaer, fylkesnemnd або інше.',
),
),
'pl' =>
array (
'display_name' => 'Przetłumacz dokument prawny',
'description' => 'Przetłumacz tekst dotyczący prawa rodzinnego w Norwegii z adnotacjami terminologii prawnej.',
'params' =>
array (
'text' => 'Tekst do przetłumaczenia.',
'source_lang' => 'Język tekstu wejściowego. Użyj auto, aby wykryć automatycznie.',
'target_lang' => 'Język, na który należy przetłumaczyć (wymagane): en, no, uk lub pl.',
'doc_type' => 'Wskazówka dotycząca typu dokumentu dla terminologii prawnej: auto, barnevernet, adopsjon, emergency, samvaer, fylkesnemnd lub inne.',
),
),
),
'dbn.legal_analysis' =>
array (
'en' =>
array (
'display_name' => 'Legal analysis',
'description' => 'Extract legal issues from a document and answer each with DBN legal context.',
'params' =>
array (
'text' => 'The document text to analyze.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'doc_type' => 'Document type hint: auto, barnevernet, adopsjon, emergency, samvaer, fylkesnemnd, or other.',
),
),
'no' =>
array (
'display_name' => 'Juridisk analyse',
'description' => 'Hent ut juridiske spørsmål fra et dokument og svar på hver med DBN juridisk kontekst.',
'params' =>
array (
'text' => 'Dokumentteksten som skal analyseres.',
'language' => 'Svarspråk: en, no, uk, pl, eller auto.',
'doc_type' => 'Dokumenttypehint: auto, barnevernet, adopsjon, akutt, samvær, fylkesnemnd, eller annet.',
),
),
'uk' =>
array (
'display_name' => 'Юридичний аналіз',
'description' => 'Витягти юридичні питання з документа та відповісти на кожне з контекстом DBN.',
'params' =>
array (
'text' => 'Текст документа для аналізу.',
'language' => 'Мова відповіді: en, no, uk, pl або auto.',
'doc_type' => 'Підказка щодо типу документа: auto, barnevernet, adopsjon, emergency, samvaer, fylkesnemnd або інше.',
),
),
'pl' =>
array (
'display_name' => 'Analiza prawna',
'description' => 'Wyodrębnij kwestie prawne z dokumentu i odpowiedz na każdą z kontekstem prawnym DBN.',
'params' =>
array (
'text' => 'Tekst dokumentu do analizy.',
'language' => 'Język odpowiedzi: en, no, uk, pl lub auto.',
'doc_type' => 'Wskazówka dotycząca typu dokumentu: auto, barnevernet, adopsjon, emergency, samvaer, fylkesnemnd lub inne.',
),
),
),
'dbn.korrespond' =>
array (
'en' =>
array (
'display_name' => 'Draft authority correspondence',
'description' => 'Draft a reply or new letter to Norwegian authorities.',
'params' =>
array (
'narrative' => 'Description of your situation — what happened and what you need.',
'received_text' => 'Text of the letter or decision you received (for reply mode).',
'recipient_body' => 'The authority you are writing to (e.g. Barnevernet, NAV, Skole, Kommune).',
'goal' => 'Your legal goal — what you want the letter to achieve.',
'mode' => 'reply (responding to a received letter) or initiate (starting new correspondence).',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
'force_draft' => 'Skip the clarify gate and draft immediately (true/false).',
),
),
'no' =>
array (
'display_name' => 'Utkast til myndighetskorrespondanse',
'description' => 'Utkast et svar eller nytt brev til norske myndigheter.',
'params' =>
array (
'narrative' => 'Beskrivelse av din situasjon — hva som skjedde og hva du trenger.',
'received_text' => 'Tekst av brevet eller vedtaket du mottok (for svarmodus).',
'recipient_body' => 'Myndigheten du skriver til (f.eks. Barnevernet, NAV, Skole, Kommune).',
'goal' => 'Ditt juridiske mål — hva du ønsker at brevet skal oppnå.',
'mode' => 'svar (respondere på et mottatt brev) eller initiere (starte ny korrespondanse).',
'language' => 'Svarspråk: en, no, uk, pl, eller auto.',
'use_case_context' => 'Inkluder kontekst fra din Sak Arbeidsbenk økt (true/false).',
'force_draft' => 'Hopp over avklaringsporten og utkast umiddelbart (true/false).',
),
),
'uk' =>
array (
'display_name' => 'Проект кореспонденції з органами влади',
'description' => 'Складіть відповідь або новий лист до норвезьких органів влади.',
'params' =>
array (
'narrative' => 'Опис вашої ситуації — що сталося і що вам потрібно.',
'received_text' => 'Текст листа або рішення, яке ви отримали (для режиму відповіді).',
'recipient_body' => 'Орган влади, до якого ви пишете (наприклад, Barnevernet, NAV, Skole, Kommune).',
'goal' => 'Ваша юридична мета — що ви хочете досягти листом.',
'mode' => 'відповідь (реагування на отриманий лист) або ініціювати (початок нової кореспонденції).',
'language' => 'Мова відповіді: en, no, uk, pl або auto.',
'use_case_context' => 'Включити контекст з вашої сесії Case Workbench (так/ні).',
'force_draft' => 'Пропустити етап уточнення та одразу скласти проект (так/ні).',
),
),
'pl' =>
array (
'display_name' => 'Szkic korespondencji z organami',
'description' => 'Szkic odpowiedzi lub nowego listu do norweskich organów.',
'params' =>
array (
'narrative' => 'Opis twojej sytuacji — co się wydarzyło i czego potrzebujesz.',
'received_text' => 'Treść listu lub decyzji, którą otrzymałeś (w trybie odpowiedzi).',
'recipient_body' => 'Organ, do którego piszesz (np. Barnevernet, NAV, Skole, Kommune).',
'goal' => 'Twój cel prawny — co chcesz osiągnąć dzięki temu listowi.',
'mode' => 'odpowiedź (odpowiadając na otrzymany list) lub inicjacja (rozpoczynając nową korespondencję).',
'language' => 'Język odpowiedzi: en, no, uk, pl lub auto.',
'use_case_context' => 'Uwzględnij kontekst z sesji Case Workbench (prawda/fałsz).',
'force_draft' => 'Pomiń bramkę wyjaśniającą i natychmiast stwórz szkic (prawda/fałsz).',
),
),
),
'dbn.barnevernet_analyze' =>
array (
'en' =>
array (
'display_name' => 'Analyze Barnevernet document',
'description' => 'Analyze child-welfare documents for red flags and legal issues.',
'params' =>
array (
'document_text' => 'Full text of the child welfare document to analyze (required).',
'filename' => 'Original filename for context (helps identify document type).',
'advocate_role' => 'Your role in the case: parent, lawyer, guardian ad litem, etc.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
),
),
'no' =>
array (
'display_name' => 'Analyser Barnevernet-dokument',
'description' => 'Analyser barnevern-dokumenter for røde flagg og juridiske problemer.',
'params' =>
array (
'document_text' => 'Full tekst av barnevern-dokumentet som skal analyseres (påkrevd).',
'filename' => 'Original filnavn for kontekst (hjelper med å identifisere dokumenttype).',
'advocate_role' => 'Din rolle i saken: forelder, advokat, vergemål, osv.',
'language' => 'Svarspråk: en, no, uk, pl, eller auto.',
'use_case_context' => 'Inkluder kontekst fra din Sak Arbeidsbenk økt (true/false).',
),
),
'uk' =>
array (
'display_name' => 'Аналіз документа Barnevernet',
'description' => 'Аналізуйте документи з питань захисту дітей на наявність червоних прапорців та юридичних проблем.',
'params' =>
array (
'document_text' => 'Повний текст документа з питань захисту дітей для аналізу (обов\'язково).',
'filename' => 'Оригінальна назва файлу для контексту (допомагає визначити тип документа).',
'advocate_role' => 'Ваша роль у справі: батько, адвокат, опікун, призначений судом тощо.',
'language' => 'Мова відповіді: en, no, uk, pl або auto.',
'use_case_context' => 'Включити контекст з вашої сесії Case Workbench (так/ні).',
),
),
'pl' =>
array (
'display_name' => 'Analiza dokumentu Barnevernet',
'description' => 'Analiza dokumentów dotyczących opieki nad dziećmi w poszukiwaniu czerwonych flag i problemów prawnych.',
'params' =>
array (
'document_text' => 'Pełny tekst dokumentu dotyczącego opieki nad dziećmi do analizy (wymagane).',
'filename' => 'Oryginalna nazwa pliku dla kontekstu (pomaga zidentyfikować typ dokumentu).',
'advocate_role' => 'Twoja rola w sprawie: rodzic, prawnik, kurator ad litem itp.',
'language' => 'Język odpowiedzi: en, no, uk, pl lub auto.',
'use_case_context' => 'Uwzględnij kontekst z sesji Case Workbench (prawda/fałsz).',
),
),
),
'dbn.advocate_brief' =>
array (
'en' =>
array (
'display_name' => 'Create advocate brief',
'description' => 'Generate a source-grounded brief for a chosen party or role.',
'params' =>
array (
'query' => 'Legal question or topic for the brief.',
'paste_text' => 'Document text to use as the basis for the brief.',
'advocate_role' => 'The party or role to advocate for (required). E.g. parent, child, Barnevernet.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
),
),
'no' =>
array (
'display_name' => 'Opprett advokatnotat',
'description' => 'Generer et kildebasert notat for en valgt part eller rolle.',
'params' =>
array (
'query' => 'Juridisk spørsmål eller tema for notatet.',
'paste_text' => 'Dokumenttekst som skal brukes som grunnlag for brevet.',
'advocate_role' => 'Parten eller rollen som skal ivaretas (obligatorisk). F.eks. forelder, barn, Barnevernet.',
'language' => 'Svarspråk: en, no, uk, pl, eller auto.',
'use_case_context' => 'Inkluder kontekst fra din Case Workbench-økt (sann/usann).',
),
),
'uk' =>
array (
'display_name' => 'Створити бриф адвоката',
'description' => 'Сгенеруйте бриф, оснований на джерелах, для обраної сторони або ролі.',
'params' =>
array (
'query' => 'Юридичне питання або тема для брифу.',
'paste_text' => 'Текст документа, який буде використано як основу для брифу.',
'advocate_role' => 'Сторона або роль, за яку потрібно виступити (обов\'язково). Наприклад, батько, дитина, Barnevernet.',
'language' => 'Мова відповіді: en, no, uk, pl або auto.',
'use_case_context' => 'Включити контекст з вашої сесії Case Workbench (так/ні).',
),
),
'pl' =>
array (
'display_name' => 'Utwórz notatkę adwokacką',
'description' => 'Generuj notatkę opartą na źródłach dla wybranej strony lub roli.',
'params' =>
array (
'query' => 'Pytanie prawne lub temat notatki.',
'paste_text' => 'Tekst dokumentu do wykorzystania jako podstawa dla briefu.',
'advocate_role' => 'Strona lub rola, którą należy reprezentować (wymagana). Np. rodzic, dziecko, Barnevernet.',
'language' => 'Język odpowiedzi: en, no, uk, pl lub auto.',
'use_case_context' => 'Uwzględnij kontekst z sesji Case Workbench (prawda/fałsz).',
),
),
),
'dbn.deep_research' =>
array (
'en' =>
array (
'display_name' => 'Deep research',
'description' => 'Expand a legal question into research angles and synthesize a cited brief.',
'params' =>
array (
'query' => 'Legal question to research in depth.',
'paste_text' => 'Supporting document text to research from.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
),
),
'no' =>
array (
'display_name' => 'Dyp forskning',
'description' => 'Utvid et juridisk spørsmål til forskningsvinkler og syntetiser et siterbart brev.',
'params' =>
array (
'query' => 'Juridisk spørsmål for dypere forskning.',
'paste_text' => 'Støttedokumenttekst for forskning.',
'language' => 'Svarspråk: en, no, uk, pl, eller auto.',
'use_case_context' => 'Inkluder kontekst fra din Case Workbench-økt (sann/usann).',
),
),
'uk' =>
array (
'display_name' => 'Глибоке дослідження',
'description' => 'Розширити юридичне питання в дослідницькі кути та синтезувати цитований бриф.',
'params' =>
array (
'query' => 'Юридичне питання для глибокого дослідження.',
'paste_text' => 'Текст підтримуючого документа для дослідження.',
'language' => 'Мова відповіді: en, no, uk, pl або auto.',
'use_case_context' => 'Включити контекст з вашої сесії Case Workbench (так/ні).',
),
),
'pl' =>
array (
'display_name' => 'Dogłębne badania',
'description' => 'Rozwiń pytanie prawne w kierunkach badawczych i zsyntetyzuj cytowany brief.',
'params' =>
array (
'query' => 'Pytanie prawne do zbadania w szczegółach.',
'paste_text' => 'Tekst dokumentu wspierającego do badania.',
'language' => 'Język odpowiedzi: en, no, uk, pl lub auto.',
'use_case_context' => 'Uwzględnij kontekst z sesji Case Workbench (prawda/fałsz).',
),
),
),
'dbn.discrepancy_find' =>
array (
'en' =>
array (
'display_name' => 'Find document discrepancies',
'description' => 'Compare two document versions for contradictions, deletions, and added claims.',
'params' =>
array (
'document_a_text' => 'Text of the first document (required).',
'document_b_text' => 'Text of the second document to compare against the first (required).',
'filename_a' => 'Filename of the first document (for reference in the report).',
'filename_b' => 'Filename of the second document (for reference in the report).',
'language' => 'Response language: en, no, uk, pl, or auto.',
),
),
'no' =>
array (
'display_name' => 'Finn dokumentavvik',
'description' => 'Sammenlign to dokumentversjoner for motsetninger, slettinger og tillegg.',
'params' =>
array (
'document_a_text' => 'Tekst av det første dokumentet (obligatorisk).',
'document_b_text' => 'Tekst av det andre dokumentet som skal sammenlignes med det første (obligatorisk).',
'filename_a' => 'Filnavn på det første dokumentet (for referanse i rapporten).',
'filename_b' => 'Filnavn på det andre dokumentet (for referanse i rapporten).',
'language' => 'Svarspråk: en, no, uk, pl, eller auto.',
),
),
'uk' =>
array (
'display_name' => 'Знайти розбіжності в документах',
'description' => 'Порівняти дві версії документа на наявність суперечностей, видалень та доданих вимог.',
'params' =>
array (
'document_a_text' => 'Текст першого документа (обов\'язково).',
'document_b_text' => 'Текст другого документа для порівняння з першим (обов\'язково).',
'filename_a' => 'Ім\'я файлу першого документа (для посилання в звіті).',
'filename_b' => 'Ім\'я файлу другого документа (для посилання в звіті).',
'language' => 'Мова відповіді: en, no, uk, pl або auto.',
),
),
'pl' =>
array (
'display_name' => 'Znajdź niezgodności w dokumentach',
'description' => 'Porównaj dwie wersje dokumentu pod kątem sprzeczności, usunięć i dodanych roszczeń.',
'params' =>
array (
'document_a_text' => 'Tekst pierwszego dokumentu (wymagany).',
'document_b_text' => 'Tekst drugiego dokumentu do porównania z pierwszym (wymagany).',
'filename_a' => 'Nazwa pliku pierwszego dokumentu (do odniesienia w raporcie).',
'filename_b' => 'Nazwa pliku drugiego dokumentu (do odniesienia w raporcie).',
'language' => 'Język odpowiedzi: en, no, uk, pl lub auto.',
),
),
),
'dbn.transcribe_audio' =>
array (
'en' =>
array (
'display_name' => 'Transcribe audio',
'description' => 'Transcribe an audio file from base64-encoded content or a URL.',
'params' =>
array (
'audio_base64' => 'Base64-encoded audio file content. Use either this or audio_url.',
'audio_url' => 'URL to a publicly accessible audio file. Use either this or audio_base64.',
'filename' => 'Original filename with extension (e.g. recording.mp3) — helps set the correct audio format.',
'language' => 'Language spoken in the audio (e.g. no, en, uk). Leave blank for auto-detection.',
'diarize' => 'Enable speaker diarization — label each segment with a speaker identifier (true/false).',
),
),
'no' =>
array (
'display_name' => 'Transkribere lyd',
'description' => 'Transkribere en lydfil fra base64-kodet innhold eller en URL.',
'params' =>
array (
'audio_base64' => 'Base64-kodet lydfilinnhold. Bruk enten dette eller audio_url.',
'audio_url' => 'URL til en offentlig tilgjengelig lydfil. Bruk enten dette eller audio_base64.',
'filename' => 'Original filnavn med filtype (f.eks. opptak.mp3) — hjelper med å sette riktig lydformat.',
'language' => 'Språk som snakkes i lyden (f.eks. no, en, uk). La stå tomt for automatisk deteksjon.',
'diarize' => 'Aktiver taler diarization — merk hvert segment med en taleridentifikator (sann/usann).',
),
),
'uk' =>
array (
'display_name' => 'Транскрибувати аудіо',
'description' => 'Транскрибувати аудіофайл з вмісту, закодованого в base64, або з URL.',
'params' =>
array (
'audio_base64' => 'Вміст аудіофайлу, закодованого в base64. Використовуйте або це, або audio_url.',
'audio_url' => 'URL до публічно доступного аудіофайлу. Використовуйте або це, або audio_base64.',
'filename' => 'Оригінальна назва файлу з розширенням (наприклад, recording.mp3) — допомагає встановити правильний формат аудіо.',
'language' => 'Мова, що звучить в аудіо (наприклад, no, en, uk). Залиште порожнім для автоматичного визначення.',
'diarize' => 'Увімкнути діаризацію спікерів — позначити кожен сегмент ідентифікатором спікера (true/false).',
),
),
'pl' =>
array (
'display_name' => 'Transkrypcja audio',
'description' => 'Transkrybuj plik audio z treści zakodowanej w base64 lub z URL.',
'params' =>
array (
'audio_base64' => 'Treść pliku audio zakodowanego w base64. Użyj tego lub audio_url.',
'audio_url' => 'URL do publicznie dostępnego pliku audio. Użyj tego lub audio_base64.',
'filename' => 'Oryginalna nazwa pliku z rozszerzeniem (np. recording.mp3) — pomaga ustawić poprawny format audio.',
'language' => 'Język mówiony w nagraniu (np. no, en, uk). Pozostaw puste dla automatycznego wykrywania.',
'diarize' => 'Włącz diarizację mówców — oznacz każdy segment identyfikatorem mówcy (prawda/fałsz).',
),
),
),
'dbn.corpus_stats' =>
array (
'en' =>
array (
'display_name' => 'Corpus statistics',
'description' => 'Return document and chunk counts and active legal sources in the DBN corpus.',
'params' =>
array (
),
),
'no' =>
array (
'display_name' => 'Korpusstatistikk',
'description' => 'Returner dokument- og delmengdeantall samt aktive juridiske kilder i DBN-korpuset.',
'params' =>
array (
),
),
'uk' =>
array (
'display_name' => 'Статистика корпусу',
'description' => 'Повернути кількість документів і частин, а також активні юридичні джерела в корпусі DBN.',
'params' =>
array (
),
),
'pl' =>
array (
'display_name' => 'Statystyki korpusu',
'description' => 'Zwróć liczbę dokumentów i fragmentów oraz aktywne źródła prawne w korpusie DBN.',
'params' =>
array (
),
),
),
'dbn.list_documents' =>
array (
'en' =>
array (
'display_name' => 'List corpus documents',
'description' => 'List DBN legal corpus documents with optional filters.',
'params' =>
array (
'category' => 'Filter by document category (e.g. barnevernet, statsforvalter, echr).',
'title' => 'Filter by title — partial match.',
'limit' => 'Maximum number of documents to return (150, default 20).',
'offset' => 'Number of documents to skip (for pagination).',
),
),
'no' =>
array (
'display_name' => 'List opp korpusdokumenter',
'description' => 'List opp DBN juridiske korpusdokumenter med valgfrie filtre.',
'params' =>
array (
'category' => 'Filtrer etter dokumentkategori (f.eks. barnevernet, statsforvalter, echr).',
'title' => 'Filtrer etter tittel — delvis samsvar.',
'limit' => 'Maksimalt antall dokumenter som skal returneres (150, standard 20).',
'offset' => 'Antall dokumenter som skal hoppes over (for paginering).',
),
),
'uk' =>
array (
'display_name' => 'Список документів корпусу',
'description' => 'Список юридичних документів корпусу DBN з необов\'язковими фільтрами.',
'params' =>
array (
'category' => 'Фільтрувати за категорією документа (наприклад, barnevernet, statsforvalter, echr).',
'title' => 'Фільтрувати за назвою — часткове співпадіння.',
'limit' => 'Максимальна кількість документів для повернення (1–50, за замовчуванням 20).',
'offset' => 'Кількість документів, які потрібно пропустити (для пагінації).',
),
),
'pl' =>
array (
'display_name' => 'Lista dokumentów korpusu',
'description' => 'Lista dokumentów prawnych korpusu DBN z opcjonalnymi filtrami.',
'params' =>
array (
'category' => 'Filtruj według kategorii dokumentu (np. barnevernet, statsforvalter, echr).',
'title' => 'Filtruj według tytułu — częściowe dopasowanie.',
'limit' => 'Maksymalna liczba dokumentów do zwrócenia (150, domyślnie 20).',
'offset' => 'Liczba dokumentów do pominięcia (do paginacji).',
),
),
),
'dbn.get_document' =>
array (
'en' =>
array (
'display_name' => 'Get document chunks',
'description' => 'Fetch a document and its text chunks by numeric document ID.',
'params' =>
array (
'document_id' => 'The numeric ID of the document to retrieve (required).',
),
),
'no' =>
array (
'display_name' => 'Hent dokumentdeler',
'description' => 'Hent et dokument og dets tekstdeler ved hjelp av numerisk dokument-ID.',
'params' =>
array (
'document_id' => 'Den numeriske ID-en til dokumentet som skal hentes (påkrevd).',
),
),
'uk' =>
array (
'display_name' => 'Отримати частини документа',
'description' => 'Отримати документ та його текстові частини за числовим ідентифікатором документа.',
'params' =>
array (
'document_id' => 'Числовий ідентифікатор документа для отримання (обов\'язково).',
),
),
'pl' =>
array (
'display_name' => 'Pobierz fragmenty dokumentu',
'description' => 'Pobierz dokument i jego fragmenty tekstowe według numerycznego ID dokumentu.',
'params' =>
array (
'document_id' => 'Numeryczne ID dokumentu do pobrania (wymagane).',
),
),
),
'dbn.citation_graph' =>
array (
'en' =>
array (
'display_name' => 'Explore citation graph',
'description' => 'Explore cites, cited-by, implementation, or chain relationships.',
'params' =>
array (
'doc_id' => 'The numeric ID of the document to explore (required).',
'action' => 'Relationship to traverse: cites, cited_by, implements, or chain (full citation path).',
'depth' => 'How many levels of relationships to explore (13, default 1).',
),
),
'no' =>
array (
'display_name' => 'Utforsk siteringsgraf',
'description' => 'Utforsk siteringer, siterte av, implementering eller kjede relasjoner.',
'params' =>
array (
'doc_id' => 'Den numeriske ID-en til dokumentet som skal utforskes (påkrevd).',
'action' => 'Relasjon å traversere: siterer, siteres_av, implementerer, eller kjede (full siteringsbane).',
'depth' => 'Hvor mange nivåer av relasjoner som skal utforskes (13, standard 1).',
),
),
'uk' =>
array (
'display_name' => 'Дослідити граф посилань',
'description' => 'Дослідити посилання, посилання на, реалізацію або ланцюгові відносини.',
'params' =>
array (
'doc_id' => 'Числовий ідентифікатор документа для дослідження (обов\'язково).',
'action' => 'Відношення для проходження: cites, cited_by, implements, або chain (повний шлях цитування).',
'depth' => 'Скільки рівнів відносин досліджувати (1–3, за замовчуванням 1).',
),
),
'pl' =>
array (
'display_name' => 'Eksploruj graf cytatów',
'description' => 'Eksploruj cytaty, cytowane przez, wdrożenie lub relacje łańcuchowe.',
'params' =>
array (
'doc_id' => 'Numeryczne ID dokumentu do eksploracji (wymagane).',
'action' => 'Relacja do przeszukania: cytaty, cytowane_przez, wdraża, lub łańcuch (pełna ścieżka cytatu).',
'depth' => 'Ile poziomów relacji do eksploracji (13, domyślnie 1).',
),
),
),
'dbn.case_workbench_plan' =>
array (
'en' =>
array (
'display_name' => 'Plan next case step',
'description' => 'Create a stateless legal preparation plan based on your situation.',
'params' =>
array (
'situation' => 'Describe your current legal situation and what you need help with (required).',
'goal' => 'Your desired outcome or legal goal.',
'deadline' => 'Any relevant deadline (date or description, e.g. "3 weeks").',
'language' => 'Response language: en, no, uk, pl, or auto.',
),
),
'no' =>
array (
'display_name' => 'Planlegg neste sakstrinn',
'description' => 'Lag en statsløs juridisk forberedelsesplan basert på din situasjon.',
'params' =>
array (
'situation' => 'Beskriv din nåværende juridiske situasjon og hva du trenger hjelp med (obligatorisk).',
'goal' => 'Ditt ønskede utfall eller juridiske mål.',
'deadline' => 'Eventuell relevant frist (dato eller beskrivelse, f.eks. "3 uker").',
'language' => 'Svarspråk: en, no, uk, pl, eller auto.',
),
),
'uk' =>
array (
'display_name' => 'Плануйте наступний крок у справі',
'description' => 'Створіть бездержавний план юридичної підготовки на основі вашої ситуації.',
'params' =>
array (
'situation' => 'Опишіть вашу поточну юридичну ситуацію та те, з чим вам потрібна допомога (обов\'язково).',
'goal' => 'Ваш бажаний результат або юридична мета.',
'deadline' => 'Будь-який відповідний термін (дата або опис, наприклад, "3 тижні").',
'language' => 'Мова відповіді: en, no, uk, pl або auto.',
),
),
'pl' =>
array (
'display_name' => 'Zaplanuj następny krok w sprawie',
'description' => 'Utwórz bezstanowy plan przygotowania prawnego na podstawie swojej sytuacji.',
'params' =>
array (
'situation' => 'Opisz swoją obecną sytuację prawną i z czym potrzebujesz pomocy (wymagane).',
'goal' => 'Twój pożądany wynik lub cel prawny.',
'deadline' => 'Jakikolwiek istotny termin (data lub opis, np. "3 tygodnie").',
'language' => 'Język odpowiedzi: en, no, uk, pl lub auto.',
),
),
),
'dbn.save_to_case' =>
array (
'en' =>
array (
'display_name' => 'Save result to My Case',
'description' => 'Explicitly save a prior MCP tool result to the user case record.',
'params' =>
array (
'tool' => 'The MCP tool slug whose result you are saving (required).',
'title' => 'A descriptive title for this saved result.',
'input_payload' => 'The input parameters used to generate the result (required).',
'output_payload' => 'The tool result to save (required).',
'meta' => 'Optional metadata (e.g. notes, tags, source reference).',
),
),
'no' =>
array (
'display_name' => 'Lagre resultat til Min Sak',
'description' => 'Lagre eksplisitt et tidligere MCP-verktøyresultat til brukerens sakspost.',
'params' =>
array (
'tool' => 'Slug til MCP-verktøyet hvis resultat du lagrer (obligatorisk).',
'title' => 'En beskrivende tittel for dette lagrede resultatet.',
'input_payload' => 'Inndata-parametrene som ble brukt for å generere resultatet (obligatorisk).',
'output_payload' => 'Verktøyresultatet som skal lagres (obligatorisk).',
'meta' => 'Valgfri metadata (f.eks. notater, tagger, kildehenvisning).',
),
),
'uk' =>
array (
'display_name' => 'Зберегти результат у Моя справа',
'description' => 'Явно зберегти попередній результат інструменту MCP у записі справи користувача.',
'params' =>
array (
'tool' => 'Слаг інструменту MCP, результат якого ви зберігаєте (обов\'язково).',
'title' => 'Описовий заголовок для цього збереженого результату.',
'input_payload' => 'Вхідні параметри, використані для генерації результату (обов\'язково).',
'output_payload' => 'Результат інструменту для збереження (обов\'язково).',
'meta' => 'Додаткова метадані (наприклад, нотатки, теги, посилання на джерело).',
),
),
'pl' =>
array (
'display_name' => 'Zapisz wynik do Mojej Sprawy',
'description' => 'Wyraźnie zapisz wcześniejszy wynik narzędzia MCP w rejestrze sprawy użytkownika.',
'params' =>
array (
'tool' => 'Slug narzędzia MCP, którego wynik zapisujesz (wymagane).',
'title' => 'Opisowy tytuł dla tego zapisanego wyniku.',
'input_payload' => 'Parametry wejściowe użyte do wygenerowania wyniku (wymagane).',
'output_payload' => 'Wynik narzędzia do zapisania (wymagane).',
'meta' => 'Opcjonalne metadane (np. notatki, tagi, odniesienie do źródła).',
),
),
),
);
+833
View File
@@ -0,0 +1,833 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/bootstrap.php';
require_once __DIR__ . '/includes/DbnMcpRuntime.php';
$uiLang = dbnToolsCurrentLanguage();
// ── Load tool from ?tool= param ──────────────────────────────────────────────
$requestedSlug = (string)($_GET['tool'] ?? '');
$toolCatalog = DbnMcpRuntime::tools();
$toolDef = null;
foreach ($toolCatalog as $t) {
if (($t['slug'] ?? '') === $requestedSlug) {
$toolDef = $t;
break;
}
}
if ($toolDef === null) {
http_response_code(404);
echo '<!doctype html><html><head><title>Tool not found</title></head><body><p>MCP tool not found. <a href="/mcp.php">Back to MCP setup</a></p></body></html>';
exit;
}
// ── Load translations ─────────────────────────────────────────────────────────
$toolTranslations = [];
$toolTransFile = __DIR__ . '/includes/mcp-tool-translations.php';
if (file_exists($toolTransFile)) {
$toolTranslations = (array)include $toolTransFile;
}
function mcpToolT(string $slug, string $field, string $lang, array $trans): string
{
$fallback = $trans[$slug]['en'][$field] ?? '';
return (string)($trans[$slug][$lang][$field] ?? $fallback);
}
function mcpToolParamT(string $slug, string $param, string $lang, array $trans): string
{
$fallback = $trans[$slug]['en']['params'][$param] ?? '';
return (string)($trans[$slug][$lang]['params'][$param] ?? $fallback);
}
// ── Resolved display data ────────────────────────────────────────────────────
$slug = (string)($toolDef['slug'] ?? '');
$displayName = mcpToolT($slug, 'display_name', $uiLang, $toolTranslations) ?: (string)($toolDef['display_name'] ?? $slug);
$description = mcpToolT($slug, 'description', $uiLang, $toolTranslations) ?: (string)($toolDef['description'] ?? '');
$props = (array)($toolDef['config']['input_schema']['properties'] ?? []);
$required = (array)($toolDef['config']['input_schema']['required'] ?? []);
$toolIcons = [
'dbn.search_legal' => '🔍',
'dbn.ask' => '💬',
'dbn.summarize' => '📋',
'dbn.timeline' => '📅',
'dbn.redact' => '🔒',
'dbn.translate' => '🌍',
'dbn.legal_analysis' => '⚖️',
'dbn.korrespond' => '✉️',
'dbn.barnevernet_analyze' => '📄',
'dbn.advocate_brief' => '🏛️',
'dbn.deep_research' => '🔬',
'dbn.discrepancy_find' => '🔄',
'dbn.transcribe_audio' => '🎤',
'dbn.corpus_stats' => '📊',
'dbn.list_documents' => '📚',
'dbn.get_document' => '📖',
'dbn.citation_graph' => '🔗',
'dbn.case_workbench_plan' => '🗂️',
'dbn.save_to_case' => '💾',
];
$toolIcon = $toolIcons[$slug] ?? '🔧';
// ── Example requests (per tool) ───────────────────────────────────────────────
$exampleRequests = [
'dbn.search_legal' => <<<'JSON'
{
"query": "emergency care order requirements barnevernet",
"language": "en",
"limit": 5,
"corpus_scope": "shared"
}
JSON,
'dbn.ask' => <<<'JSON'
{
"question": "Can Barnevernet remove a child without a court hearing?",
"language": "en",
"use_case_context": false
}
JSON,
'dbn.summarize' => <<<'JSON'
{
"text": "Paste the case document text here...",
"language": "en",
"use_legal_corpus": true,
"use_case_context": false
}
JSON,
'dbn.timeline' => <<<'JSON'
{
"text": "Paste case notes or decision letter here...",
"language": "en",
"focus": "all",
"use_case_context": false
}
JSON,
'dbn.redact' => <<<'JSON'
{
"text": "On 15.03.2024 caseworker Anna Hansen met with...",
"language": "no",
"mode": "standard",
"output_format": "contextual"
}
JSON,
'dbn.translate' => <<<'JSON'
{
"text": "Barnevernet fattet vedtak om omsorgsovertakelse...",
"source_lang": "no",
"target_lang": "en",
"doc_type": "barnevernet"
}
JSON,
'dbn.legal_analysis' => <<<'JSON'
{
"text": "Paste the document text to analyze here...",
"language": "en",
"doc_type": "auto"
}
JSON,
'dbn.korrespond' => <<<'JSON'
{
"narrative": "Barnevernet issued an emergency placement order without notification.",
"recipient_body": "Barnevernet",
"goal": "Appeal the decision and request access to case documents.",
"mode": "initiate",
"language": "en"
}
JSON,
'dbn.barnevernet_analyze' => <<<'JSON'
{
"document_text": "Paste the Barnevernet document text here...",
"filename": "vedtak-omsorgsovertakelse.pdf",
"advocate_role": "parent",
"language": "en"
}
JSON,
'dbn.advocate_brief' => <<<'JSON'
{
"query": "Grounds for challenging a care order under BVL §4-12",
"advocate_role": "parent",
"language": "en",
"use_case_context": false
}
JSON,
'dbn.deep_research' => <<<'JSON'
{
"query": "ECHR Article 8 violations in Norwegian child welfare cases",
"language": "en",
"use_case_context": false
}
JSON,
'dbn.discrepancy_find' => <<<'JSON'
{
"document_a_text": "Text of the first document version...",
"document_b_text": "Text of the second document version...",
"filename_a": "report-2023.pdf",
"filename_b": "report-2024.pdf",
"language": "en"
}
JSON,
'dbn.transcribe_audio' => <<<'JSON'
{
"audio_url": "https://example.com/meeting-recording.mp3",
"language": "no",
"diarize": true
}
JSON,
'dbn.corpus_stats' => '{}',
'dbn.list_documents' => <<<'JSON'
{
"category": "barnevernet",
"limit": 20,
"offset": 0
}
JSON,
'dbn.get_document' => <<<'JSON'
{
"document_id": 42
}
JSON,
'dbn.citation_graph' => <<<'JSON'
{
"doc_id": 42,
"action": "cites",
"depth": 2
}
JSON,
'dbn.case_workbench_plan' => <<<'JSON'
{
"situation": "Barnevernet has scheduled a Fylkesnemnda hearing in 3 weeks. I need to prepare a response.",
"goal": "Challenge the care order and request return of the child.",
"deadline": "2024-06-15",
"language": "en"
}
JSON,
'dbn.save_to_case' => <<<'JSON'
{
"tool": "dbn.legal_analysis",
"title": "Legal analysis of care order decision",
"input_payload": { "text": "...", "language": "en" },
"output_payload": { "issues": [...], "summary": "..." }
}
JSON,
];
// ── Example responses (per tool) ──────────────────────────────────────────────
$exampleResponses = [
'dbn.search_legal' => <<<'JSON'
{
"ok": true,
"results": [
{
"title": "BVL §4-12 — Vedtak om omsorgsovertakelse",
"chunk_text": "§ 4-12. Vedtak om å overta omsorgen for et barn kan treffes dersom...",
"score": 0.91,
"source": "barnevernsloven"
},
{
"title": "NOU 2012:5 — Bedre beskyttelse av barns utvikling",
"chunk_text": "Utvalget vurderer at terskelen for akuttvedtak bør klargjøres...",
"score": 0.84,
"source": "nou"
}
],
"total": 2
}
JSON,
'dbn.ask' => <<<'JSON'
{
"ok": true,
"answer": "Under Norwegian law, Barnevernet may issue an emergency care order (akuttvedtak) under BVL §4-6 without a prior court hearing if there is immediate risk of serious harm. However, the case must be reviewed by Fylkesnemnda within six weeks...",
"sources": [
{ "title": "BVL §4-6 — Midlertidige vedtak", "url": "/corpus/bvl-4-6" },
{ "title": "Strand Lobben m.fl. mot Norge (EMD)", "url": "/corpus/echr-strand-lobben" }
],
"credits_used": 3
}
JSON,
'dbn.summarize' => <<<'JSON'
{
"ok": true,
"summary": "The document is a Barnevernet investigation report concerning a family with two children. Key findings include...",
"key_points": [
"Home visit conducted 12.03.2024 — no immediate danger found",
"Tiltaksplan proposed with three support measures",
"Next review scheduled for 15.06.2024"
],
"credits_used": 5
}
JSON,
'dbn.timeline' => <<<'JSON'
{
"ok": true,
"events": [
{
"date": "2024-03-12",
"date_type": "ABSOLUTE",
"confidence": "HIGH",
"actor": "Barnevernet",
"description": "Home visit conducted following anonymous report",
"source_excerpt": "12.03.2024: hjemmebesøk ble gjennomført..."
},
{
"date": "2024-06-15",
"date_type": "ABSOLUTE",
"confidence": "HIGH",
"actor": "Fylkesnemnda",
"description": "Scheduled hearing date",
"source_excerpt": "Nemnda berammer møte til 15. juni 2024"
}
],
"next_practical_step": "File your written submission before the 06.06.2024 deadline.",
"credits_used": 4
}
JSON,
'dbn.redact' => <<<'JSON'
{
"ok": true,
"redacted_text": "On [DATE A] caseworker [PERSON A] met with [PERSON B] at [ADDRESS A]...",
"replacements": [
{ "original": "15.03.2024", "replacement": "[DATE A]" },
{ "original": "Anna Hansen", "replacement": "[PERSON A]" },
{ "original": "Ola Nordmann", "replacement": "[PERSON B]" }
],
"credits_used": 2
}
JSON,
'dbn.translate' => <<<'JSON'
{
"ok": true,
"source_lang": "no",
"target_lang": "en",
"translated_text": "Barnevernet issued a care order decision (vedtak om omsorgsovertakelse)...",
"annotations": [
{
"term": "omsorgsovertakelse",
"note": "Care order — formal transfer of parental responsibility to the state under BVL §4-12"
},
{
"term": "Fylkesnemnda",
"note": "County Social Welfare Board — the quasi-judicial body that reviews Barnevernet decisions"
}
],
"credits_used": 6
}
JSON,
'dbn.legal_analysis' => <<<'JSON'
{
"ok": true,
"issues": [
{
"issue": "Procedural rights under forvaltningsloven §17",
"analysis": "The parent was not given an opportunity to be heard before the decision was made, potentially violating fvl §17...",
"legal_basis": ["forvaltningsloven §17", "BVL §6-3"],
"sources": [{ "title": "fvl §17 — Plikten til å påse at saken er opplyst" }]
}
],
"overall_assessment": "The document shows potential procedural violations that may form grounds for an appeal.",
"credits_used": 8
}
JSON,
'dbn.korrespond' => <<<'JSON'
{
"ok": true,
"letter_no": "Til Barnevernet\nDato: 24.05.2024\n\nAnke på vedtak om omsorgsovertakelse...",
"letter_en": "To Barnevernet\nDate: 24.05.2024\n\nAppeal against care order decision...",
"cited_sources": ["BVL §4-12", "forvaltningsloven §28", "EMK artikkel 8"],
"credits_used": 10
}
JSON,
'dbn.barnevernet_analyze' => <<<'JSON'
{
"ok": true,
"red_flags": [
"Decision issued without notification to parent (fvl §16 violation)",
"No tiltaksplan offered before escalating to care order (BVL §4-4)"
],
"legal_issues": [
"Potential violation of proportionality principle — less intrusive measures not exhausted",
"ECHR Art. 8 family life rights not addressed in reasoning"
],
"recommendations": [
"File an appeal within 3 weeks citing fvl §28",
"Request full case file under fvl §18",
"Consult Fylkesnemnda procedural timeline"
],
"credits_used": 7
}
JSON,
'dbn.advocate_brief' => <<<'JSON'
{
"ok": true,
"brief": "On behalf of the parent, the following legal arguments support challenging the care order under BVL §4-12...",
"legal_arguments": [
"The threshold for care order under §4-12(a) requires 'serious deficiencies' — the report does not meet this standard",
"Proportionality requires exhausting support measures (§4-4) before removal"
],
"sources": [
{ "title": "BVL §4-12", "type": "statute" },
{ "title": "Rt-2015-110", "type": "case_law" }
],
"credits_used": 9
}
JSON,
'dbn.deep_research' => <<<'JSON'
{
"ok": true,
"research_angles": [
"Procedural safeguards under ECHR Art. 6 in child welfare proceedings",
"Substantive requirements for care order threshold (BVL §4-12)",
"Norwegian Supreme Court interpretation of best interests standard"
],
"brief": "Research synthesis: The European Court of Human Rights has found Norway in violation of Art. 8 in 14 cases since 2015, with the core issue being...",
"citations": [
"Strand Lobben m.fl. mot Norge, EMD-37283/13 (10.09.2019)",
"Johansen mot Norge, EMD-17383/90 (07.08.1996)"
],
"credits_used": 15
}
JSON,
'dbn.discrepancy_find' => <<<'JSON'
{
"ok": true,
"discrepancies": [
{
"type": "deletion",
"location": "Section 3, paragraph 2",
"original": "The child expressed a wish to return home.",
"modified": "[removed]",
"significance": "HIGH — child's stated preference is a legally relevant factor under BVL §6-3"
},
{
"type": "added_claim",
"location": "Section 5",
"original": "[absent]",
"modified": "The parent failed to attend the scheduled meeting.",
"significance": "MEDIUM — unverified claim added in the later version"
}
],
"credits_used": 6
}
JSON,
'dbn.transcribe_audio' => <<<'JSON'
{
"ok": true,
"transcript": "Full transcript of the audio recording...",
"segments": [
{ "speaker": "A", "start": 0.0, "end": 4.2, "text": "God morgen, vi er samlet for å diskutere..." },
{ "speaker": "B", "start": 4.5, "end": 9.1, "text": "Takk. Jeg ønsker å starte med å si at..." }
],
"language_detected": "no",
"credits_used": 8
}
JSON,
'dbn.corpus_stats' => <<<'JSON'
{
"ok": true,
"documents": 2847,
"chunks": 94231,
"sources": {
"statutes": 42,
"tribunal_decisions": 1204,
"echr_cases": 187,
"government_reports": 1414
},
"last_updated": "2024-05-01"
}
JSON,
'dbn.list_documents' => <<<'JSON'
{
"ok": true,
"documents": [
{ "id": 42, "title": "BVL §4-12 — Vedtak om omsorgsovertakelse", "category": "statute" },
{ "id": 87, "title": "FNV-2023-142 — Fylkesnemnda for barnevern og sosiale saker", "category": "tribunal" },
{ "id": 203, "title": "Strand Lobben m.fl. mot Norge (EMD-37283/13)", "category": "echr" }
],
"total": 847,
"offset": 0
}
JSON,
'dbn.get_document' => <<<'JSON'
{
"ok": true,
"document": {
"id": 42,
"title": "BVL §4-12 — Vedtak om omsorgsovertakelse",
"category": "statute",
"chunks": [
{ "chunk_id": 1, "text": "§ 4-12. Vedtak om å overta omsorgen for et barn kan treffes dersom..." },
{ "chunk_id": 2, "text": "(a) det er alvorlige mangler ved den daglige omsorg som barnet får..." }
]
}
}
JSON,
'dbn.citation_graph' => <<<'JSON'
{
"ok": true,
"action": "cites",
"root": { "id": 42, "title": "BVL §4-12" },
"nodes": [
{ "id": 42, "title": "BVL §4-12", "type": "statute" },
{ "id": 15, "title": "BVL §4-4 — Hjelpetiltak", "type": "statute" },
{ "id": 203, "title": "Strand Lobben m.fl. mot Norge", "type": "echr" }
],
"edges": [
{ "from": 42, "to": 15, "type": "cites" },
{ "from": 42, "to": 203, "type": "cites" }
],
"depth": 1
}
JSON,
'dbn.case_workbench_plan' => <<<'JSON'
{
"ok": true,
"plan": {
"situation_summary": "Fylkesnemnda hearing scheduled in 3 weeks — care order challenge.",
"steps": [
"1. Request full case file under forvaltningsloven §18 — submit by 01.06.2024",
"2. Run Legal Analysis tool on the original care order decision",
"3. Create Advocate Brief (parent role) with the grounds for challenge",
"4. Draft formal response letter using Korrespond (Fylkesnemnda preset)",
"5. Compile timeline of all procedural dates"
],
"recommended_tools": ["dbn.legal_analysis", "dbn.advocate_brief", "dbn.korrespond", "dbn.timeline"],
"deadline": "2024-06-15"
},
"credits_used": 3
}
JSON,
'dbn.save_to_case' => <<<'JSON'
{
"ok": true,
"saved": true,
"case_entry_id": "entry_abc123",
"title": "Legal analysis of care order decision",
"saved_at": "2024-05-24T09:15:00Z"
}
JSON,
];
$exReq = trim($exampleRequests[$slug] ?? '{}');
$exResp = trim($exampleResponses[$slug] ?? '{"ok":true}');
?>
<!doctype html>
<html lang="<?= htmlspecialchars($uiLang) ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= htmlspecialchars($displayName) ?> — <?= htmlspecialchars(dbnToolsT('mcp_page_title', $uiLang)) ?></title>
<meta name="robots" content="noindex, nofollow">
<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-tool-breadcrumb {
font-size: 0.85rem;
color: var(--muted, #667085);
margin-bottom: 1.5rem;
}
.mcp-tool-breadcrumb a {
color: var(--dbn-blue, #00205b);
text-decoration: none;
}
.mcp-tool-breadcrumb a:hover { text-decoration: underline; }
.mcp-tool-hero {
margin-bottom: 0.5rem;
}
.mcp-tool-hero__icon {
font-size: 2rem;
margin-bottom: 0.5rem;
}
.mcp-tool-hero__name {
font-family: 'Crimson Pro', serif;
font-size: clamp(1.5rem, 3vw, 2rem);
font-weight: 700;
color: var(--dbn-blue, #00205b);
margin: 0 0 0.2rem;
}
.mcp-tool-hero__slug {
font-family: 'IBM Plex Mono', 'Courier New', monospace;
font-size: 0.82rem;
color: var(--muted, #667085);
margin-bottom: 0.75rem;
}
.mcp-tool-hero__desc {
font-size: 1rem;
color: var(--dbn-ink, #16130f);
max-width: 640px;
margin: 0;
}
/* Param table */
.mcp-param-table {
width: 100%;
border-collapse: collapse;
font-size: 0.875rem;
margin-top: 0.75rem;
}
.mcp-param-table th {
text-align: left;
padding: 0.5rem 0.75rem;
background: var(--dbn-paper, #f6f2ea);
font-weight: 600;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: .04em;
color: var(--muted, #667085);
border-bottom: 2px solid var(--dbn-line, #e5e7eb);
}
.mcp-param-table td {
padding: 0.6rem 0.75rem;
border-bottom: 1px solid var(--dbn-line, #e5e7eb);
vertical-align: top;
}
.mcp-param-table tr:last-child td { border-bottom: 0; }
.mcp-param-name {
font-family: 'IBM Plex Mono', monospace;
font-size: 0.82rem;
}
.mcp-param-name--required {
background: #ddd6fe;
color: #5b21b6;
padding: 0.15rem 0.4rem;
border-radius: 4px;
}
.mcp-param-name--optional {
background: #f3f4f6;
color: #374151;
padding: 0.15rem 0.4rem;
border-radius: 4px;
}
.mcp-param-type {
color: var(--muted, #667085);
font-size: 0.8rem;
}
.mcp-param-enum {
font-size: 0.75rem;
color: var(--muted, #667085);
font-family: monospace;
}
.mcp-req-yes {
color: #065f46;
font-weight: 600;
}
.mcp-req-no {
color: var(--muted, #667085);
}
/* Code blocks (reuse mcp.php style) */
.mcp-code-wrap {
position: relative;
}
.mcp-code-wrap pre {
background: #101828;
color: #f9fafb;
padding: 1rem;
border-radius: 8px;
font-size: 0.8rem;
overflow-x: auto;
margin: 0;
line-height: 1.6;
}
.mcp-copy-btn {
position: absolute;
top: 0.5rem;
right: 0.5rem;
background: rgba(249,250,251,.12);
border: 1px solid rgba(249,250,251,.2);
color: #f9fafb;
font-size: 0.75rem;
padding: 0.3rem 0.6rem;
border-radius: 5px;
cursor: pointer;
transition: background .15s;
}
.mcp-copy-btn:hover { background: rgba(249,250,251,.22); }
.mcp-code-label {
font-size: 0.78rem;
font-weight: 600;
color: var(--muted, #667085);
text-transform: uppercase;
letter-spacing: .04em;
margin: 0 0 0.4rem;
}
.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); }
.mcp-connect-link {
display: inline-block;
margin-top: 0.75rem;
color: var(--dbn-blue, #00205b);
font-weight: 600;
font-size: 0.9rem;
text-decoration: none;
}
.mcp-connect-link:hover { text-decoration: underline; }
</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">
<!-- ── Breadcrumb ────────────────────────────────────────────── -->
<div class="mcp-tool-breadcrumb">
<a href="/mcp.php"><?= htmlspecialchars(dbnToolsT('mcp_tool_back', $uiLang)) ?></a>
</div>
<!-- ── Hero ──────────────────────────────────────────────────── -->
<section class="account-section">
<div class="mcp-tool-hero">
<div class="mcp-tool-hero__icon"><?= $toolIcon ?></div>
<h1 class="mcp-tool-hero__name"><?= htmlspecialchars($displayName) ?></h1>
<div class="mcp-tool-hero__slug"><?= htmlspecialchars($slug) ?></div>
<p class="mcp-tool-hero__desc"><?= htmlspecialchars($description) ?></p>
</div>
</section>
<!-- ── Parameters ────────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('mcp_tool_params_title', $uiLang)) ?></p>
<?php if (empty($props)): ?>
<p style="color:var(--muted,#667085); font-size:0.88rem;">
<?= htmlspecialchars(dbnToolsT('mcp_tool_no_params', $uiLang)) ?>
</p>
<?php else: ?>
<div style="overflow-x:auto;">
<table class="mcp-param-table">
<thead>
<tr>
<th><?= htmlspecialchars(dbnToolsT('mcp_tool_col_param', $uiLang)) ?></th>
<th><?= htmlspecialchars(dbnToolsT('mcp_tool_col_type', $uiLang)) ?></th>
<th><?= htmlspecialchars(dbnToolsT('mcp_tool_col_required', $uiLang)) ?></th>
<th><?= htmlspecialchars(dbnToolsT('mcp_tool_col_desc', $uiLang)) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($props as $paramName => $paramDef):
$isReq = in_array($paramName, $required, true);
$paramType = (string)($paramDef['type'] ?? 'string');
$enumVals = (array)($paramDef['enum'] ?? []);
$paramDesc = mcpToolParamT($slug, $paramName, $uiLang, $toolTranslations);
if ($paramDesc === '') {
$paramDesc = (string)($paramDef['description'] ?? '');
}
?>
<tr>
<td>
<span class="mcp-param-name <?= $isReq ? 'mcp-param-name--required' : 'mcp-param-name--optional' ?>">
<?= htmlspecialchars($paramName) ?><?= $isReq ? '*' : '' ?>
</span>
</td>
<td>
<span class="mcp-param-type"><?= htmlspecialchars($paramType) ?></span>
<?php if (!empty($enumVals)): ?>
<br><span class="mcp-param-enum"><?= htmlspecialchars(implode(' | ', $enumVals)) ?></span>
<?php endif; ?>
</td>
<td>
<?php if ($isReq): ?>
<span class="mcp-req-yes"><?= htmlspecialchars(dbnToolsT('mcp_tool_yes', $uiLang)) ?></span>
<?php else: ?>
<span class="mcp-req-no">—</span>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($paramDesc) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<p style="font-size:0.75rem; color:var(--muted,#667085); margin-top:0.5rem;">
* <?= htmlspecialchars(dbnToolsT('mcp_tools_param_req_hint', $uiLang)) ?>
</p>
<?php endif; ?>
</section>
<!-- ── Example request ───────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('mcp_tool_example_req', $uiLang)) ?></p>
<p class="mcp-code-label">POST /api/mcp/user/tools/<?= htmlspecialchars($slug) ?>/invoke</p>
<div class="mcp-code-wrap">
<button class="mcp-copy-btn" type="button" id="copyReqBtn">Copy</button>
<pre><code id="exReqCode"><?= htmlspecialchars($exReq) ?></code></pre>
</div>
</section>
<!-- ── Example response ──────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('mcp_tool_example_resp', $uiLang)) ?></p>
<div class="mcp-code-wrap">
<button class="mcp-copy-btn" type="button" id="copyRespBtn">Copy</button>
<pre><code id="exRespCode"><?= htmlspecialchars($exResp) ?></code></pre>
</div>
</section>
<!-- ── Connect ───────────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('mcp_tool_connect_title', $uiLang)) ?></p>
<p style="color:var(--muted,#667085); font-size:0.88rem; margin-top:0;">
<?= htmlspecialchars(dbnToolsT('mcp_tool_connect_text', $uiLang)) ?>
</p>
<a href="/mcp.php" class="mcp-connect-link"><?= htmlspecialchars(dbnToolsT('mcp_tool_setup_link', $uiLang)) ?></a>
</section>
<!-- ── Privacy ───────────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('mcp_privacy_title', $uiLang)) ?></p>
<div class="mcp-privacy">
<strong><?= htmlspecialchars(dbnToolsT('mcp_privacy_text', $uiLang)) ?></strong>
</div>
<p style="margin-top:0.75rem; font-size:0.82rem; color:var(--muted,#667085);">
<?= htmlspecialchars(dbnToolsT('mcp_privacy_legal', $uiLang)) ?>
</p>
</section>
</div><!-- /.account-shell -->
<?php require_once __DIR__ . '/includes/footer.php'; ?>
<script>
(function () {
'use strict';
function copyText(text, btn) {
var orig = btn.textContent;
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(function () {
btn.textContent = 'Copied!';
setTimeout(function () { btn.textContent = orig; }, 1500);
});
} else {
var ta = document.createElement('textarea');
ta.value = text;
ta.style.cssText = 'position:fixed;opacity:0';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
btn.textContent = 'Copied!';
setTimeout(function () { btn.textContent = orig; }, 1500);
}
}
var reqBtn = document.getElementById('copyReqBtn');
var respBtn = document.getElementById('copyRespBtn');
var reqCode = document.getElementById('exReqCode');
var respCode = document.getElementById('exRespCode');
if (reqBtn && reqCode) reqBtn.addEventListener('click', function () { copyText(reqCode.textContent, reqBtn); });
if (respBtn && respCode) respBtn.addEventListener('click', function () { copyText(respCode.textContent, respBtn); });
}());
</script>
</body>
</html>
+70 -38
View File
@@ -50,8 +50,8 @@ $toolIcons = [
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MCP — Do Better Norge</title>
<meta name="description" content="Connect Claude, Cursor, and other AI tools to all 19 Do Better Norge legal preparation tools via MCP.">
<title><?= htmlspecialchars(dbnToolsT('mcp_page_title', $uiLang)) ?></title>
<meta name="description" content="<?= htmlspecialchars(dbnToolsT('mcp_meta_desc', $uiLang)) ?>">
<meta name="robots" content="noindex, nofollow">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@@ -190,7 +190,16 @@ $toolIcons = [
-webkit-box-orient: vertical;
overflow: hidden;
}
.mcp-tool-card[open] .mcp-tool-card__desc { -webkit-line-clamp: unset; overflow: visible; }
.mcp-tool-card[data-expanded="1"] .mcp-tool-card__desc { -webkit-line-clamp: unset; overflow: visible; }
.mcp-tool-card__details-link {
display: inline-block;
margin-top: 0.5rem;
font-size: 0.78rem;
color: var(--dbn-blue, #00205b);
font-weight: 600;
text-decoration: none;
}
.mcp-tool-card__details-link:hover { text-decoration: underline; }
/* ── Gated states ───────────────────────────────────────────── */
.mcp-gate {
text-align: center;
@@ -259,30 +268,34 @@ window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
<!-- ── Hero ─────────────────────────────────────────────────── -->
<div class="mcp-hero">
<div class="mcp-hero__badge">✦ Plus &amp; Pro</div>
<h1>Use DBN tools from Claude, Cursor &amp; Copilot</h1>
<p>Connect any MCP client to all 19 Do Better Norge tools — transcription, legal analysis, timelines, redaction, and more.</p>
<div class="mcp-hero__badge"><?= htmlspecialchars(dbnToolsT('mcp_hero_badge', $uiLang)) ?></div>
<h1><?= htmlspecialchars(dbnToolsT('mcp_hero_h1', $uiLang)) ?></h1>
<p><?= htmlspecialchars(dbnToolsT('mcp_hero_sub', $uiLang)) ?></p>
</div>
<!-- ── Token management ─────────────────────────────────────── -->
<section class="account-section" id="mcpTokenSection">
<p class="account-section__title">Your MCP token</p>
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('mcp_token_section_title', $uiLang)) ?></p>
<?php if (!$isLoggedIn): ?>
<div class="mcp-gate">
<p>Sign in to create your personal MCP token for Plus or Pro members.</p>
<a href="/?return=<?= urlencode('/mcp.php') ?>" class="account-upgrade-btn" style="display:inline-block; text-decoration:none;">Sign in</a>
<p><?= htmlspecialchars(dbnToolsT('mcp_gate_guest_p', $uiLang)) ?></p>
<a href="/?return=<?= urlencode('/mcp.php') ?>" class="account-upgrade-btn" style="display:inline-block; text-decoration:none;">
<?= htmlspecialchars(dbnToolsT('mcp_gate_guest_btn', $uiLang)) ?>
</a>
</div>
<?php elseif (!$isPlusPro): ?>
<div class="mcp-gate">
<p>MCP access is available on Plus and Pro plans. Upgrade to connect your AI tools.</p>
<a href="/account.php" class="account-upgrade-btn" style="display:inline-block; text-decoration:none;">Upgrade plan</a>
<p><?= htmlspecialchars(dbnToolsT('mcp_gate_free_p', $uiLang)) ?></p>
<a href="/account.php" class="account-upgrade-btn" style="display:inline-block; text-decoration:none;">
<?= htmlspecialchars(dbnToolsT('mcp_gate_free_btn', $uiLang)) ?>
</a>
</div>
<?php else: ?>
<p style="color:var(--muted,#667085); font-size:0.88rem; margin-top:0;">
Tokens are shown once at creation. Create one per client (Claude, Cursor, VS Code…).
<?= htmlspecialchars(dbnToolsT('mcp_token_hint', $uiLang)) ?>
</p>
<div id="mcpTokenList" style="margin:0.85rem 0 0.25rem;"></div>
@@ -290,23 +303,27 @@ window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
<form id="mcpTokenForm" style="display:flex; gap:0.5rem; flex-wrap:wrap; align-items:center; margin-top:0.85rem;">
<input id="mcpTokenName" type="text" maxlength="100" value="DBN MCP" aria-label="Token name"
style="min-width:220px; padding:0.65rem 0.75rem; border:1px solid var(--line,#d0d5dd); border-radius:8px; font-size:0.9rem;">
<button type="submit" class="account-upgrade-btn" style="border:0; cursor:pointer;">Create token</button>
<button type="submit" class="account-upgrade-btn" style="border:0; cursor:pointer;">
<?= htmlspecialchars(dbnToolsT('mcp_token_create_btn', $uiLang)) ?>
</button>
</form>
<div id="mcpTokenReveal" class="mcp-token-reveal">
<strong>Copy this token now — it won't be shown again:</strong>
<strong><?= htmlspecialchars(dbnToolsT('mcp_token_reveal_label', $uiLang)) ?></strong>
<code id="mcpTokenPlain"></code>
<button id="mcpTokenCopyBtn" type="button" style="margin-top:0.6rem; border:1px solid #bbf7d0; background:#fff; border-radius:6px; padding:0.35rem 0.7rem; cursor:pointer; font-size:0.82rem;">Copy token</button>
<button id="mcpTokenCopyBtn" type="button" style="margin-top:0.6rem; border:1px solid #bbf7d0; background:#fff; border-radius:6px; padding:0.35rem 0.7rem; cursor:pointer; font-size:0.82rem;">
<?= htmlspecialchars(dbnToolsT('mcp_token_copy_btn', $uiLang)) ?>
</button>
</div>
<?php endif; ?>
</section>
<!-- ── Client config ─────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title">Client configuration</p>
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('mcp_config_title', $uiLang)) ?></p>
<p style="color:var(--muted,#667085); font-size:0.88rem; margin-top:0; margin-bottom:1rem;">
Paste your token into the config below after creating it above.
<span id="tokenHint" style="display:none;color:#065f46;font-weight:600;"> Token auto-filled.</span>
<?= htmlspecialchars(dbnToolsT('mcp_config_hint', $uiLang)) ?>
<span id="tokenHint" style="display:none;color:#065f46;font-weight:600;"> <?= htmlspecialchars(dbnToolsT('mcp_config_token_filled', $uiLang)) ?></span>
</p>
<div class="mcp-tabs" role="tablist" aria-label="MCP client">
@@ -343,7 +360,7 @@ window.DBN_TOOLS_LANG = <?= json_encode($uiLang, JSON_UNESCAPED_UNICODE) ?>;
<!-- Claude Code -->
<div class="mcp-tab-panel" id="tab-claude-code" role="tabpanel">
<p style="font-size:0.85rem; color:var(--muted,#667085); margin:0 0 0.75rem;">
Run in your terminal:
<?= htmlspecialchars(dbnToolsT('mcp_config_run_terminal', $uiLang)) ?>
</p>
<div class="mcp-code-wrap">
<button class="mcp-copy-btn" type="button" data-copy="claude-code">Copy</button>
@@ -427,7 +444,7 @@ Authorization: Bearer dbn_user_mcp_...</code></pre>
<div style="margin-top:1.25rem; display:flex; align-items:center; gap:0.75rem; flex-wrap:wrap;">
<button id="mcpTestBtn" type="button"
style="border:1px solid var(--dbn-blue,#00205b); color:var(--dbn-blue,#00205b); background:#fff; border-radius:8px; padding:0.55rem 1rem; cursor:pointer; font-size:0.88rem;">
Test connection
<?= htmlspecialchars(dbnToolsT('mcp_test_btn', $uiLang)) ?>
</button>
<span id="mcpTestResult" style="font-size:0.88rem;"></span>
</div>
@@ -436,9 +453,9 @@ Authorization: Bearer dbn_user_mcp_...</code></pre>
<!-- ── Tool catalog ───────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title">Available tools (<?= count($toolCatalog) ?>)</p>
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('mcp_tools_title', $uiLang)) ?> (<?= count($toolCatalog) ?>)</p>
<p style="color:var(--muted,#667085); font-size:0.88rem; margin-top:0;">
All tools run on your Plus or Pro plan credits. Click a card for full details.
<?= htmlspecialchars(dbnToolsT('mcp_tools_sub', $uiLang)) ?>
</p>
<div class="mcp-tool-grid" id="mcpToolGrid">
@@ -450,6 +467,7 @@ Authorization: Bearer dbn_user_mcp_...</code></pre>
$props = (array)($tool['config']['input_schema']['properties'] ?? []);
$req = (array)($tool['config']['input_schema']['required'] ?? []);
$paramNames = array_keys($props);
$detailUrl = '/mcp-tool.php?tool=' . urlencode((string)($tool['slug'] ?? '')) . '&lang=' . urlencode($uiLang);
?>
<div class="mcp-tool-card" data-slug="<?= $slug ?>">
<div class="mcp-tool-card__icon"><?= $icon ?></div>
@@ -465,10 +483,14 @@ Authorization: Bearer dbn_user_mcp_...</code></pre>
font-family: monospace;"><?= htmlspecialchars($param) ?><?= in_array($param, $req, true) ? '*' : '' ?></span>
<?php endforeach; ?>
</div>
<p style="margin:0.5rem 0 0; font-size:0.75rem; color:var(--muted,#667085); display:none;" class="mcp-tool-card__hint">
<em>Purple = required</em>
<p style="margin:0.3rem 0 0; font-size:0.75rem; color:var(--muted,#667085); display:none;" class="mcp-tool-card__hint">
<em><?= htmlspecialchars(dbnToolsT('mcp_tools_param_req_hint', $uiLang)) ?></em>
</p>
<?php endif; ?>
<a href="<?= htmlspecialchars($detailUrl) ?>" class="mcp-tool-card__details-link" style="display:none;"
onclick="event.stopPropagation();">
<?= htmlspecialchars(dbnToolsT('mcp_tools_view_details', $uiLang)) ?>
</a>
</div>
<?php endforeach; ?>
</div>
@@ -476,15 +498,12 @@ Authorization: Bearer dbn_user_mcp_...</code></pre>
<!-- ── Privacy notice ─────────────────────────────────────────── -->
<section class="account-section">
<p class="account-section__title">Privacy</p>
<p class="account-section__title"><?= htmlspecialchars(dbnToolsT('mcp_privacy_title', $uiLang)) ?></p>
<div class="mcp-privacy">
<strong>Process-and-forget by default.</strong> All tool calls process your text in memory and return results to your AI client.
Nothing is saved to My Case unless you explicitly call <code>dbn.save_to_case</code>.
DBN MCP tokens do not retain input text or tool results after the request completes.
<strong><?= htmlspecialchars(dbnToolsT('mcp_privacy_text', $uiLang)) ?></strong>
</div>
<p style="margin-top:1rem; font-size:0.82rem; color:var(--muted,#667085);">
Tools provide legal preparation support, not final legal advice.
Results are for informational purposes and should be reviewed by a qualified legal professional.
<?= htmlspecialchars(dbnToolsT('mcp_privacy_legal', $uiLang)) ?>
</p>
</section>
@@ -496,6 +515,17 @@ Authorization: Bearer dbn_user_mcp_...</code></pre>
(function () {
'use strict';
/* ── i18n from PHP ─────────────────────────────────────────── */
var I = {
tokenActive: <?= json_encode(dbnToolsT('mcp_token_active', $uiLang)) ?>,
tokenRevoked: <?= json_encode(dbnToolsT('mcp_token_revoked', $uiLang)) ?>,
tokenNeverUsed: <?= json_encode(dbnToolsT('mcp_token_never_used', $uiLang)) ?>,
tokenLastUsed: <?= json_encode(dbnToolsT('mcp_token_last_used', $uiLang)) ?>,
tokenRevokeBtn: <?= json_encode(dbnToolsT('mcp_token_revoke_btn', $uiLang)) ?>,
testNoToken: <?= json_encode(dbnToolsT('mcp_test_no_token', $uiLang)) ?>,
testTesting: <?= json_encode(dbnToolsT('mcp_test_testing', $uiLang)) ?>,
};
/* ── Helpers ──────────────────────────────────────────────────── */
function esc(s) {
return String(s || '').replace(/[&<>"']/g, function (c) {
@@ -569,15 +599,18 @@ Authorization: Bearer dbn_user_mcp_...</code></pre>
var params = card.querySelector('.mcp-tool-card__params');
var hint = card.querySelector('.mcp-tool-card__hint');
var desc = card.querySelector('.mcp-tool-card__desc');
var details = card.querySelector('.mcp-tool-card__details-link');
if (expanded) {
card.setAttribute('data-expanded', '0');
if (params) params.style.display = 'none';
if (hint) hint.style.display = 'none';
if (details) details.style.display = 'none';
if (desc) { desc.style.webkitLineClamp = '2'; desc.style.overflow = 'hidden'; }
} else {
card.setAttribute('data-expanded', '1');
if (params) params.style.display = 'flex';
if (hint) hint.style.display = 'block';
if (details) details.style.display = 'inline-block';
if (desc) { desc.style.webkitLineClamp = 'unset'; desc.style.overflow = 'visible'; }
}
});
@@ -590,20 +623,20 @@ Authorization: Bearer dbn_user_mcp_...</code></pre>
function renderTokens(tokens) {
if (!tokenList) return;
if (!tokens.length) {
tokenList.innerHTML = '<p style="color:var(--muted,#667085); font-size:0.88rem;">No MCP tokens yet.</p>';
tokenList.innerHTML = '<p style="color:var(--muted,#667085); font-size:0.88rem;"><?= addslashes(dbnToolsT('mcp_token_no_tokens', $uiLang)) ?></p>';
return;
}
tokenList.innerHTML = tokens.map(function (t) {
var status = t.is_active ? 'Active' : 'Revoked';
var last = t.last_used_at ? 'Last used ' + esc(t.last_used_at) : 'Never used';
var status = t.is_active ? esc(I.tokenActive) : esc(I.tokenRevoked);
var last = t.last_used_at ? esc(I.tokenLastUsed) + ' ' + esc(t.last_used_at) : esc(I.tokenNeverUsed);
var revoke = t.is_active
? '<button type="button" class="mcp-token-row__revoke" data-revoke="' + t.id + '">Revoke</button>'
? '<button type="button" class="mcp-token-row__revoke" data-revoke="' + t.id + '">' + esc(I.tokenRevokeBtn) + '</button>'
: '';
return '<div class="mcp-token-row">'
+ '<div>'
+ '<strong>' + esc(t.name) + '</strong><br>'
+ '<code style="font-size:0.8rem;">' + esc(t.token_prefix) + '…</code><br>'
+ '<span style="color:var(--muted,#667085); font-size:0.78rem;">' + esc(status) + ' · ' + esc(last) + '</span>'
+ '<span style="color:var(--muted,#667085); font-size:0.78rem;">' + status + ' · ' + last + '</span>'
+ '</div>'
+ revoke
+ '</div>';
@@ -657,7 +690,6 @@ Authorization: Bearer dbn_user_mcp_...</code></pre>
if (reveal) reveal.style.display = 'block';
fillToken(tok);
loadTokens();
// Scroll to config
var configSection = document.querySelector('.mcp-tabs');
if (configSection) configSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
})
@@ -678,11 +710,11 @@ Authorization: Bearer dbn_user_mcp_...</code></pre>
if (testBtn) {
testBtn.addEventListener('click', function () {
if (!activeToken) {
if (testResult) { testResult.textContent = 'Create a token first.'; testResult.style.color = '#b45309'; }
if (testResult) { testResult.textContent = I.testNoToken; testResult.style.color = '#b45309'; }
return;
}
testBtn.disabled = true;
if (testResult) { testResult.textContent = 'Testing…'; testResult.style.color = 'var(--muted,#667085)'; }
if (testResult) { testResult.textContent = I.testTesting; testResult.style.color = 'var(--muted,#667085)'; }
fetch('/api/mcp/user/session', {
headers: { 'Authorization': 'Bearer ' + activeToken }
})
+463
View File
@@ -0,0 +1,463 @@
<?php
/**
* generate-mcp-translations.php
* One-shot CLI script: php scripts/generate-mcp-translations.php
*
* Produces two files:
* 1. translations/mcp-chrome.php — UI chrome strings for mcp.php + mcp-tool.php
* 2. includes/mcp-tool-translations.php — tool names, descriptions, param docs in all 4 languages
*/
declare(strict_types=1);
define('SCRIPT_MODE', true);
require_once dirname(__DIR__) . '/includes/bootstrap.php';
// ─────────────────────────────────────────────────────────────────────────────
// Azure config (same pattern as generate-page-translations.php)
// ─────────────────────────────────────────────────────────────────────────────
$azConfig = [
'endpoint' => rtrim((string)dbnToolsEnv('DBN_AZURE_OPENAI_ENDPOINT', ''), '/'),
'api_key' => (string)dbnToolsEnv('DBN_AZURE_OPENAI_API_KEY', ''),
'api_version' => (string)dbnToolsEnv('DBN_AZURE_OPENAI_API_VERSION', '2024-02-01'),
'chat_deployment' => (string)dbnToolsEnv('DBN_AZURE_OPENAI_CHAT_DEPLOYMENT', 'gpt-4o-mini'),
];
function azureChat(array $config, array $messages, array $options = []): array
{
$url = $config['endpoint']
. '/openai/deployments/'
. rawurlencode($config['chat_deployment'])
. '/chat/completions?api-version='
. rawurlencode($config['api_version']);
$payload = json_encode(array_filter([
'messages' => $messages,
'temperature' => $options['temperature'] ?? 0.1,
'max_tokens' => $options['max_tokens'] ?? 4096,
'response_format' => isset($options['json']) && $options['json']
? ['type' => 'json_object'] : null,
], fn($v) => $v !== null), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'api-key: ' . $config['api_key'],
],
CURLOPT_TIMEOUT => (int)($options['timeout'] ?? 120),
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
]);
$body = curl_exec($ch);
$code = (int)curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$err = curl_error($ch);
curl_close($ch);
if ($body === false) throw new RuntimeException("Azure curl failed: $err");
$decoded = json_decode($body, true);
if (!is_array($decoded)) throw new RuntimeException("Azure returned non-JSON (HTTP $code)");
if ($code < 200 || $code >= 300) {
throw new RuntimeException('Azure error: ' . ($decoded['error']['message'] ?? "HTTP $code"));
}
return $decoded;
}
$languages = [
'no' => 'Norwegian (Norsk bokmål)',
'uk' => 'Ukrainian',
'pl' => 'Polish',
];
$systemPrompt = <<<'PROMPT'
You are a professional legal translator specializing in Norwegian family law and child welfare.
Translate all JSON string values to {LANG}.
PRESERVE AS-IS:
- Norwegian institution names: Barnevernet, Statsforvalteren, Bufdir, Fylkesnemnda, NAV, Tingretten
- Norwegian legal act names: forvaltningsloven, barnevernsloven, opplæringslova, EMK, fvl
- Norwegian legal terms: klage, vedtak, saksnummer, akutt, omsorgsovertakelse, tiltaksplan
- Technical terms: MCP, DBN, dbn.tool_name, JSON, Bearer token, npx, stdio
- Brand names: Do Better Norge, Claude, Cursor, VS Code, Windsurf, Copilot
- Code snippets, URLs, env var names like DBN_MCP_TOKEN
- HTML entities and tags exactly as they appear
- The symbol ← and →
Return a JSON object with EXACTLY the same keys as the input. Translate only the string values.
Use formal/professional register appropriate for a legal tools platform.
PROMPT;
function translateBatch(array $azConfig, string $langName, string $systemPrompt, array $batch): array
{
$prompt = str_replace('{LANG}', $langName, $systemPrompt);
$json = json_encode($batch, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$response = azureChat($azConfig, [
['role' => 'system', 'content' => $prompt],
['role' => 'user', 'content' => $json],
], ['json' => true, 'max_tokens' => 4096, 'timeout' => 120]);
$content = trim((string)($response['choices'][0]['message']['content'] ?? ''));
$decoded = json_decode($content, true);
if (!is_array($decoded)) {
echo " [WARN] JSON parse failed, trying extraction...\n";
if (preg_match('/\{.*\}/s', $content, $m)) {
$decoded = json_decode($m[0], true);
}
}
if (!is_array($decoded)) {
echo " [ERROR] Could not parse response, using English fallback\n";
return $batch;
}
foreach ($batch as $k => $v) {
if (!isset($decoded[$k])) $decoded[$k] = $v;
}
return $decoded;
}
function writePhpFile(string $path, array $data, string $comment): void
{
$php = "<?php\n// $comment\n// DO NOT EDIT MANUALLY — regenerate with scripts/generate-mcp-translations.php\nreturn ";
$php .= var_export($data, true);
$php .= ";\n";
file_put_contents($path, $php);
}
// ─────────────────────────────────────────────────────────────────────────────
// Part 1 — MCP UI chrome strings
// ─────────────────────────────────────────────────────────────────────────────
$chromeEn = [
// mcp.php
'mcp_page_title' => 'MCP — Do Better Norge',
'mcp_meta_desc' => 'Connect Claude, Cursor, and other AI tools to all 19 DBN legal preparation tools via MCP.',
'mcp_hero_badge' => '✦ Plus & Pro',
'mcp_hero_h1' => 'Use DBN tools from Claude, Cursor & Copilot',
'mcp_hero_sub' => 'Connect any MCP client to all 19 Do Better Norge tools — transcription, legal analysis, timelines, redaction, and more.',
'mcp_token_section_title' => 'Your MCP token',
'mcp_gate_guest_p' => 'Sign in to create your personal MCP token. Available to Plus and Pro members.',
'mcp_gate_guest_btn' => 'Sign in',
'mcp_gate_free_p' => 'MCP access is available on Plus and Pro plans. Upgrade to connect your AI tools.',
'mcp_gate_free_btn' => 'Upgrade plan',
'mcp_token_hint' => 'Tokens are shown once at creation. Create one per client (Claude, Cursor, VS Code…).',
'mcp_token_create_btn' => 'Create token',
'mcp_token_reveal_label' => 'Copy this token now — it will not be shown again:',
'mcp_token_copy_btn' => 'Copy token',
'mcp_token_no_tokens' => 'No MCP tokens yet.',
'mcp_token_active' => 'Active',
'mcp_token_revoked' => 'Revoked',
'mcp_token_never_used' => 'Never used',
'mcp_token_last_used' => 'Last used',
'mcp_token_revoke_btn' => 'Revoke',
'mcp_config_title' => 'Client configuration',
'mcp_config_hint' => 'Paste your token into the config below after creating it above.',
'mcp_config_token_filled' => 'Token auto-filled.',
'mcp_config_run_terminal' => 'Run in your terminal:',
'mcp_test_btn' => 'Test connection',
'mcp_test_no_token' => 'Create a token first.',
'mcp_test_testing' => 'Testing…',
'mcp_tools_title' => 'Available tools',
'mcp_tools_sub' => 'All tools run on your Plus or Pro plan credits. Click a card for full technical details.',
'mcp_tools_param_req_hint' => 'Purple = required',
'mcp_tools_view_details' => 'View details →',
// privacy section
'mcp_privacy_title' => 'Privacy',
'mcp_privacy_text' => 'Process-and-forget by default. All tool calls process your text in memory and return results to your AI client. Nothing is saved to My Case unless you explicitly call dbn.save_to_case.',
'mcp_privacy_legal' => 'Tools provide legal preparation support, not final legal advice. Results are for informational purposes and should be reviewed by a qualified legal professional.',
// mcp-tool.php
'mcp_tool_back' => '← Back to MCP setup',
'mcp_tool_params_title' => 'Parameters',
'mcp_tool_no_params' => 'This tool takes no input parameters.',
'mcp_tool_col_param' => 'Parameter',
'mcp_tool_col_type' => 'Type',
'mcp_tool_col_required' => 'Required',
'mcp_tool_col_desc' => 'Description',
'mcp_tool_example_req' => 'Example request',
'mcp_tool_example_resp' => 'Example response',
'mcp_tool_connect_title' => 'Connect',
'mcp_tool_connect_text' => 'Create your MCP token on the setup page and use it with any supported client.',
'mcp_tool_setup_link' => 'Set up MCP →',
'mcp_tool_yes' => 'Yes',
'mcp_tool_no' => 'No',
];
echo "\n=== Part 1: Translating MCP UI chrome strings ===\n";
$chromeTranslations = ['en' => $chromeEn];
$batchSize = 20;
$keys = array_keys($chromeEn);
$batches = array_chunk($keys, $batchSize);
foreach ($languages as $langCode => $langName) {
echo " Language: $langName ($langCode)\n";
$langResult = [];
foreach ($batches as $idx => $batchKeys) {
$batchNum = $idx + 1;
echo " Batch $batchNum/" . count($batches) . " (" . count($batchKeys) . " strings)...\n";
$batchInput = [];
foreach ($batchKeys as $k) $batchInput[$k] = $chromeEn[$k];
$langResult = array_merge($langResult, translateBatch($azConfig, $langName, $systemPrompt, $batchInput));
}
$chromeTranslations[$langCode] = $langResult;
}
$chromeOutPath = dirname(__DIR__) . '/translations/mcp-chrome.php';
writePhpFile($chromeOutPath, $chromeTranslations, 'MCP UI chrome translations — mcp.php + mcp-tool.php');
echo " Written: $chromeOutPath\n";
// ─────────────────────────────────────────────────────────────────────────────
// Part 2 — MCP tool content (display_name, description, param descriptions)
// ─────────────────────────────────────────────────────────────────────────────
// English source for all tool content — param descriptions are authored here
// since DbnMcpRuntime.php params mostly lack description fields.
$toolsEn = [
'dbn.search_legal' => [
'display_name' => 'Search DBN legal corpus',
'description' => 'Search the DBN Norwegian family-law corpus.',
'params' => [
'query' => 'The search query (minimum 3 characters). Enter a legal topic, keyword, or question.',
'language' => 'Response language: en, no, uk, pl, or auto (detect from input).',
'limit' => 'Maximum number of results to return (110).',
'corpus_scope' => 'Which corpus to search: shared (public legal corpus), private (your uploaded documents), or both.',
],
],
'dbn.ask' => [
'display_name' => 'Ask a legal question',
'description' => 'Answer a legal preparation question with source-grounded DBN context.',
'params' => [
'question' => 'The legal question to answer (minimum 5 characters).',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
],
],
'dbn.summarize' => [
'display_name' => 'Summarize document',
'description' => 'Summarize pasted case text with optional legal-corpus enrichment.',
'params' => [
'text' => 'The text to summarize.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_legal_corpus' => 'Enrich the summary with relevant legal corpus passages (true/false).',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
],
],
'dbn.timeline' => [
'display_name' => 'Extract timeline',
'description' => 'Extract dates, hearings, milestones, and deadlines from case text.',
'params' => [
'text' => 'The case text to extract dates from.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'focus' => 'What to extract: all (every date), deadlines (appeal windows and filing deadlines), hearings (tribunal and court dates), cps (Barnevernet milestones).',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
],
],
'dbn.redact' => [
'display_name' => 'Redact private data',
'description' => 'Remove or pseudonymize names, IDs, phone numbers, addresses, and places.',
'params' => [
'text' => 'The text to redact.',
'language' => 'Language of the input text: en, no, uk, pl, or auto.',
'mode' => 'Redaction scope: standard (names, IDs, phones) or strict (also addresses, locations, institutions).',
'output_format' => 'Replacement style: contextual ([PERSON A]), generic (█████), or pseudonym (consistent invented names).',
],
],
'dbn.translate' => [
'display_name' => 'Translate legal document',
'description' => 'Translate Norwegian family-law text with legal terminology annotations.',
'params' => [
'text' => 'The text to translate.',
'source_lang' => 'Language of the input text. Use auto to detect automatically.',
'target_lang' => 'Language to translate into (required): en, no, uk, or pl.',
'doc_type' => 'Document type hint for legal terminology: auto, barnevernet, adopsjon, emergency, samvaer, fylkesnemnd, or other.',
],
],
'dbn.legal_analysis' => [
'display_name' => 'Legal analysis',
'description' => 'Extract legal issues from a document and answer each with DBN legal context.',
'params' => [
'text' => 'The document text to analyze.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'doc_type' => 'Document type hint: auto, barnevernet, adopsjon, emergency, samvaer, fylkesnemnd, or other.',
],
],
'dbn.korrespond' => [
'display_name' => 'Draft authority correspondence',
'description' => 'Draft a reply or new letter to Norwegian authorities.',
'params' => [
'narrative' => 'Description of your situation — what happened and what you need.',
'received_text' => 'Text of the letter or decision you received (for reply mode).',
'recipient_body' => 'The authority you are writing to (e.g. Barnevernet, NAV, Skole, Kommune).',
'goal' => 'Your legal goal — what you want the letter to achieve.',
'mode' => 'reply (responding to a received letter) or initiate (starting new correspondence).',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
'force_draft' => 'Skip the clarify gate and draft immediately (true/false).',
],
],
'dbn.barnevernet_analyze' => [
'display_name' => 'Analyze Barnevernet document',
'description' => 'Analyze child-welfare documents for red flags and legal issues.',
'params' => [
'document_text' => 'Full text of the child welfare document to analyze (required).',
'filename' => 'Original filename for context (helps identify document type).',
'advocate_role' => 'Your role in the case: parent, lawyer, guardian ad litem, etc.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
],
],
'dbn.advocate_brief' => [
'display_name' => 'Create advocate brief',
'description' => 'Generate a source-grounded brief for a chosen party or role.',
'params' => [
'query' => 'Legal question or topic for the brief.',
'paste_text' => 'Document text to use as the basis for the brief.',
'advocate_role' => 'The party or role to advocate for (required). E.g. parent, child, Barnevernet.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
],
],
'dbn.deep_research' => [
'display_name' => 'Deep research',
'description' => 'Expand a legal question into research angles and synthesize a cited brief.',
'params' => [
'query' => 'Legal question to research in depth.',
'paste_text' => 'Supporting document text to research from.',
'language' => 'Response language: en, no, uk, pl, or auto.',
'use_case_context' => 'Include context from your Case Workbench session (true/false).',
],
],
'dbn.discrepancy_find' => [
'display_name' => 'Find document discrepancies',
'description' => 'Compare two document versions for contradictions, deletions, and added claims.',
'params' => [
'document_a_text' => 'Text of the first document (required).',
'document_b_text' => 'Text of the second document to compare against the first (required).',
'filename_a' => 'Filename of the first document (for reference in the report).',
'filename_b' => 'Filename of the second document (for reference in the report).',
'language' => 'Response language: en, no, uk, pl, or auto.',
],
],
'dbn.transcribe_audio' => [
'display_name' => 'Transcribe audio',
'description' => 'Transcribe an audio file from base64-encoded content or a URL.',
'params' => [
'audio_base64' => 'Base64-encoded audio file content. Use either this or audio_url.',
'audio_url' => 'URL to a publicly accessible audio file. Use either this or audio_base64.',
'filename' => 'Original filename with extension (e.g. recording.mp3) — helps set the correct audio format.',
'language' => 'Language spoken in the audio (e.g. no, en, uk). Leave blank for auto-detection.',
'diarize' => 'Enable speaker diarization — label each segment with a speaker identifier (true/false).',
],
],
'dbn.corpus_stats' => [
'display_name' => 'Corpus statistics',
'description' => 'Return document and chunk counts and active legal sources in the DBN corpus.',
'params' => [],
],
'dbn.list_documents' => [
'display_name' => 'List corpus documents',
'description' => 'List DBN legal corpus documents with optional filters.',
'params' => [
'category' => 'Filter by document category (e.g. barnevernet, statsforvalter, echr).',
'title' => 'Filter by title — partial match.',
'limit' => 'Maximum number of documents to return (150, default 20).',
'offset' => 'Number of documents to skip (for pagination).',
],
],
'dbn.get_document' => [
'display_name' => 'Get document chunks',
'description' => 'Fetch a document and its text chunks by numeric document ID.',
'params' => [
'document_id' => 'The numeric ID of the document to retrieve (required).',
],
],
'dbn.citation_graph' => [
'display_name' => 'Explore citation graph',
'description' => 'Explore cites, cited-by, implementation, or chain relationships.',
'params' => [
'doc_id' => 'The numeric ID of the document to explore (required).',
'action' => 'Relationship to traverse: cites, cited_by, implements, or chain (full citation path).',
'depth' => 'How many levels of relationships to explore (13, default 1).',
],
],
'dbn.case_workbench_plan' => [
'display_name' => 'Plan next case step',
'description' => 'Create a stateless legal preparation plan based on your situation.',
'params' => [
'situation' => 'Describe your current legal situation and what you need help with (required).',
'goal' => 'Your desired outcome or legal goal.',
'deadline' => 'Any relevant deadline (date or description, e.g. "3 weeks").',
'language' => 'Response language: en, no, uk, pl, or auto.',
],
],
'dbn.save_to_case' => [
'display_name' => 'Save result to My Case',
'description' => 'Explicitly save a prior MCP tool result to the user case record.',
'params' => [
'tool' => 'The MCP tool slug whose result you are saving (required).',
'title' => 'A descriptive title for this saved result.',
'input_payload' => 'The input parameters used to generate the result (required).',
'output_payload' => 'The tool result to save (required).',
'meta' => 'Optional metadata (e.g. notes, tags, source reference).',
],
],
];
echo "\n=== Part 2: Translating MCP tool content ===\n";
// Flatten all tool strings for translation (display_name, description, params)
// Key format: {short_slug}__name, {short_slug}__desc, {short_slug}__p__{param}
$flatSource = [];
$slugMap = []; // short_slug => full_slug
foreach ($toolsEn as $fullSlug => $toolData) {
$shortSlug = str_replace('dbn.', '', $fullSlug);
$slugMap[$shortSlug] = $fullSlug;
$flatSource[$shortSlug . '__name'] = $toolData['display_name'];
$flatSource[$shortSlug . '__desc'] = $toolData['description'];
foreach ($toolData['params'] as $param => $desc) {
$flatSource[$shortSlug . '__p__' . $param] = $desc;
}
}
// Translate in batches of 20
$flatKeys = array_keys($flatSource);
$batches = array_chunk($flatKeys, 20);
$flatTranslations = ['en' => $flatSource];
foreach ($languages as $langCode => $langName) {
echo " Language: $langName ($langCode)\n";
$langResult = [];
foreach ($batches as $idx => $batchKeys) {
$batchNum = $idx + 1;
echo " Batch $batchNum/" . count($batches) . " (" . count($batchKeys) . " strings)...\n";
$batchInput = [];
foreach ($batchKeys as $k) $batchInput[$k] = $flatSource[$k];
$langResult = array_merge($langResult, translateBatch($azConfig, $langName, $systemPrompt, $batchInput));
}
$flatTranslations[$langCode] = $langResult;
}
// Reconstruct nested structure: [full_slug][lang] => ['display_name', 'description', 'params']
$toolTranslations = [];
foreach ($toolsEn as $fullSlug => $toolData) {
$shortSlug = str_replace('dbn.', '', $fullSlug);
foreach (['en', 'no', 'uk', 'pl'] as $lc) {
$toolTranslations[$fullSlug][$lc] = [
'display_name' => $flatTranslations[$lc][$shortSlug . '__name'] ?? $toolData['display_name'],
'description' => $flatTranslations[$lc][$shortSlug . '__desc'] ?? $toolData['description'],
'params' => [],
];
foreach (array_keys($toolData['params']) as $param) {
$toolTranslations[$fullSlug][$lc]['params'][$param] =
$flatTranslations[$lc][$shortSlug . '__p__' . $param] ?? $toolData['params'][$param];
}
}
}
$toolOutPath = dirname(__DIR__) . '/includes/mcp-tool-translations.php';
writePhpFile($toolOutPath, $toolTranslations, 'MCP tool translations — display_name, description, param descriptions');
echo " Written: $toolOutPath\n";
echo "\n✓ Done. Next steps:\n";
echo " 1. Copy translations from translations/mcp-chrome.php into includes/i18n.php\n";
echo " 2. The tool translations are ready in includes/mcp-tool-translations.php\n";
+209
View File
@@ -0,0 +1,209 @@
<?php
// MCP UI chrome translations — mcp.php + mcp-tool.php
// DO NOT EDIT MANUALLY — regenerate with scripts/generate-mcp-translations.php
return array (
'en' =>
array (
'mcp_page_title' => 'MCP — Do Better Norge',
'mcp_meta_desc' => 'Connect Claude, Cursor, and other AI tools to all 19 DBN legal preparation tools via MCP.',
'mcp_hero_badge' => '✦ Plus & Pro',
'mcp_hero_h1' => 'Use DBN tools from Claude, Cursor & Copilot',
'mcp_hero_sub' => 'Connect any MCP client to all 19 Do Better Norge tools — transcription, legal analysis, timelines, redaction, and more.',
'mcp_token_section_title' => 'Your MCP token',
'mcp_gate_guest_p' => 'Sign in to create your personal MCP token. Available to Plus and Pro members.',
'mcp_gate_guest_btn' => 'Sign in',
'mcp_gate_free_p' => 'MCP access is available on Plus and Pro plans. Upgrade to connect your AI tools.',
'mcp_gate_free_btn' => 'Upgrade plan',
'mcp_token_hint' => 'Tokens are shown once at creation. Create one per client (Claude, Cursor, VS Code…).',
'mcp_token_create_btn' => 'Create token',
'mcp_token_reveal_label' => 'Copy this token now — it will not be shown again:',
'mcp_token_copy_btn' => 'Copy token',
'mcp_token_no_tokens' => 'No MCP tokens yet.',
'mcp_token_active' => 'Active',
'mcp_token_revoked' => 'Revoked',
'mcp_token_never_used' => 'Never used',
'mcp_token_last_used' => 'Last used',
'mcp_token_revoke_btn' => 'Revoke',
'mcp_config_title' => 'Client configuration',
'mcp_config_hint' => 'Paste your token into the config below after creating it above.',
'mcp_config_token_filled' => 'Token auto-filled.',
'mcp_config_run_terminal' => 'Run in your terminal:',
'mcp_test_btn' => 'Test connection',
'mcp_test_no_token' => 'Create a token first.',
'mcp_test_testing' => 'Testing…',
'mcp_tools_title' => 'Available tools',
'mcp_tools_sub' => 'All tools run on your Plus or Pro plan credits. Click a card for full technical details.',
'mcp_tools_param_req_hint' => 'Purple = required',
'mcp_tools_view_details' => 'View details →',
'mcp_privacy_title' => 'Privacy',
'mcp_privacy_text' => 'Process-and-forget by default. All tool calls process your text in memory and return results to your AI client. Nothing is saved to My Case unless you explicitly call dbn.save_to_case.',
'mcp_privacy_legal' => 'Tools provide legal preparation support, not final legal advice. Results are for informational purposes and should be reviewed by a qualified legal professional.',
'mcp_tool_back' => '← Back to MCP setup',
'mcp_tool_params_title' => 'Parameters',
'mcp_tool_no_params' => 'This tool takes no input parameters.',
'mcp_tool_col_param' => 'Parameter',
'mcp_tool_col_type' => 'Type',
'mcp_tool_col_required' => 'Required',
'mcp_tool_col_desc' => 'Description',
'mcp_tool_example_req' => 'Example request',
'mcp_tool_example_resp' => 'Example response',
'mcp_tool_connect_title' => 'Connect',
'mcp_tool_connect_text' => 'Create your MCP token on the setup page and use it with any supported client.',
'mcp_tool_setup_link' => 'Set up MCP →',
'mcp_tool_yes' => 'Yes',
'mcp_tool_no' => 'No',
),
'no' =>
array (
'mcp_page_title' => 'MCP — Do Better Norge',
'mcp_meta_desc' => 'Koble Claude, Cursor og andre AI-verktøy til alle 19 DBN juridiske forberedelsesverktøy via MCP.',
'mcp_hero_badge' => '✦ Plus & Pro',
'mcp_hero_h1' => 'Bruk DBN-verktøy fra Claude, Cursor & Copilot',
'mcp_hero_sub' => 'Koble enhver MCP-klient til alle 19 Do Better Norge-verktøy — transkripsjon, juridisk analyse, tidslinjer, redigering og mer.',
'mcp_token_section_title' => 'Din MCP-token',
'mcp_gate_guest_p' => 'Logg inn for å opprette din personlige MCP-token. Tilgjengelig for Plus- og Pro-medlemmer.',
'mcp_gate_guest_btn' => 'Logg inn',
'mcp_gate_free_p' => 'MCP-tilgang er tilgjengelig på Plus- og Pro-planer. Oppgrader for å koble dine AI-verktøy.',
'mcp_gate_free_btn' => 'Oppgrader plan',
'mcp_token_hint' => 'Tokens vises én gang ved opprettelse. Opprett én per klient (Claude, Cursor, VS Code…).',
'mcp_token_create_btn' => 'Opprett token',
'mcp_token_reveal_label' => 'Kopier denne token nå — den vil ikke bli vist igjen:',
'mcp_token_copy_btn' => 'Kopier token',
'mcp_token_no_tokens' => 'Ingen MCP-tokens ennå.',
'mcp_token_active' => 'Aktiv',
'mcp_token_revoked' => 'Tilbaketrukket',
'mcp_token_never_used' => 'Aldri brukt',
'mcp_token_last_used' => 'Sist brukt',
'mcp_token_revoke_btn' => 'Tilbaketrukk',
'mcp_config_title' => 'Klientkonfigurasjon',
'mcp_config_hint' => 'Lim inn tokenet ditt i konfigurasjonen nedenfor etter å ha opprettet det ovenfor.',
'mcp_config_token_filled' => 'Token automatisk fylt ut.',
'mcp_config_run_terminal' => 'Kjør i terminalen din:',
'mcp_test_btn' => 'Test tilkobling',
'mcp_test_no_token' => 'Opprett et token først.',
'mcp_test_testing' => 'Tester…',
'mcp_tools_title' => 'Tilgjengelige verktøy',
'mcp_tools_sub' => 'Alle verktøy kjører på kreditter fra din Plus- eller Pro-plan. Klikk på et kort for full teknisk informasjon.',
'mcp_tools_param_req_hint' => 'Lilla = påkrevd',
'mcp_tools_view_details' => 'Se detaljer →',
'mcp_privacy_title' => 'Personvern',
'mcp_privacy_text' => 'Prosesser og glem som standard. Alle verktøyanrop prosesserer teksten din i minnet og returnerer resultater til din AI-klient. Ingenting lagres i Min sak med mindre du eksplisitt kaller dbn.save_to_case.',
'mcp_privacy_legal' => 'Verktøyene gir støtte til juridisk forberedelse, ikke endelig juridisk rådgivning. Resultatene er kun til informasjonsformål og bør vurderes av en kvalifisert juridisk profesjonell.',
'mcp_tool_back' => '← Tilbake til MCP-oppsett',
'mcp_tool_params_title' => 'Parametere',
'mcp_tool_no_params' => 'Dette verktøyet tar ingen inndata-parametere.',
'mcp_tool_col_param' => 'Parameter',
'mcp_tool_col_type' => 'Type',
'mcp_tool_col_required' => 'Påkrevd',
'mcp_tool_col_desc' => 'Beskrivelse',
'mcp_tool_example_req' => 'Eksempel på forespørsel',
'mcp_tool_example_resp' => 'Eksempel på svar',
'mcp_tool_connect_title' => 'Koble til',
'mcp_tool_connect_text' => 'Opprett din MCP-token på oppsettssiden og bruk den med enhver støttet klient.',
'mcp_tool_setup_link' => 'Sett opp MCP →',
'mcp_tool_yes' => 'Ja',
'mcp_tool_no' => 'Nei',
),
'uk' =>
array (
'mcp_page_title' => 'MCP — Do Better Norge',
'mcp_meta_desc' => 'Підключіть Claude, Cursor та інші інструменти ШІ до всіх 19 юридичних підготовчих інструментів DBN через MCP.',
'mcp_hero_badge' => '✦ Плюс та Профі',
'mcp_hero_h1' => 'Використовуйте інструменти DBN від Claude, Cursor та Copilot',
'mcp_hero_sub' => 'Підключіть будь-якого клієнта MCP до всіх 19 інструментів Do Better Norge — транскрипція, юридичний аналіз, часові лінії, редагування та інше.',
'mcp_token_section_title' => 'Ваш токен MCP',
'mcp_gate_guest_p' => 'Увійдіть, щоб створити свій особистий токен MCP. Доступно для учасників Плюс та Профі.',
'mcp_gate_guest_btn' => 'Увійти',
'mcp_gate_free_p' => 'Доступ до MCP доступний у планах Плюс та Профі. Оновіть, щоб підключити свої інструменти ШІ.',
'mcp_gate_free_btn' => 'Оновити план',
'mcp_token_hint' => 'Токени показуються один раз під час створення. Створіть один для кожного клієнта (Claude, Cursor, VS Code…).',
'mcp_token_create_btn' => 'Створити токен',
'mcp_token_reveal_label' => 'Скопіюйте цей токен зараз — він більше не буде показаний:',
'mcp_token_copy_btn' => 'Скопіювати токен',
'mcp_token_no_tokens' => 'Ще немає токенів MCP.',
'mcp_token_active' => 'Активний',
'mcp_token_revoked' => 'Скасовано',
'mcp_token_never_used' => 'Ніколи не використовувався',
'mcp_token_last_used' => 'Останній раз використовувався',
'mcp_token_revoke_btn' => 'Скасувати',
'mcp_config_title' => 'Конфігурація клієнта',
'mcp_config_hint' => 'Вставте свій токен у конфігурацію нижче після його створення вище.',
'mcp_config_token_filled' => 'Токен автоматично заповнений.',
'mcp_config_run_terminal' => 'Запустіть у вашому терміналі:',
'mcp_test_btn' => 'Перевірити з\'єднання',
'mcp_test_no_token' => 'Спочатку створіть токен.',
'mcp_test_testing' => 'Тестування…',
'mcp_tools_title' => 'Доступні інструменти',
'mcp_tools_sub' => 'Усі інструменти працюють на кредитах вашого плану Plus або Pro. Натисніть на картку для отримання повних технічних деталей.',
'mcp_tools_param_req_hint' => 'Фіолетовий = обов\'язковий',
'mcp_tools_view_details' => 'Переглянути деталі →',
'mcp_privacy_title' => 'Конфіденційність',
'mcp_privacy_text' => 'Обробка та забуття за замовчуванням. Усі виклики інструментів обробляють ваш текст у пам\'яті та повертають результати вашому AI-клієнту. Нічого не зберігається у My Case, якщо ви явно не викликаєте dbn.save_to_case.',
'mcp_privacy_legal' => 'Інструменти надають підтримку в підготовці юридичних документів, а не остаточні юридичні поради. Результати призначені лише для інформаційних цілей і повинні бути переглянуті кваліфікованим юридичним фахівцем.',
'mcp_tool_back' => '← Повернутися до налаштування MCP',
'mcp_tool_params_title' => 'Параметри',
'mcp_tool_no_params' => 'Цей інструмент не приймає вхідних параметрів.',
'mcp_tool_col_param' => 'Параметр',
'mcp_tool_col_type' => 'Тип',
'mcp_tool_col_required' => 'Обов\'язковий',
'mcp_tool_col_desc' => 'Опис',
'mcp_tool_example_req' => 'Приклад запиту',
'mcp_tool_example_resp' => 'Приклад відповіді',
'mcp_tool_connect_title' => 'Підключити',
'mcp_tool_connect_text' => 'Створіть свій токен MCP на сторінці налаштувань і використовуйте його з будь-яким підтримуваним клієнтом.',
'mcp_tool_setup_link' => 'Налаштувати MCP →',
'mcp_tool_yes' => 'Так',
'mcp_tool_no' => 'Ні',
),
'pl' =>
array (
'mcp_page_title' => 'MCP — Do Better Norge',
'mcp_meta_desc' => 'Połącz Claude, Cursor i inne narzędzia AI z wszystkimi 19 narzędziami przygotowania prawnego DBN za pośrednictwem MCP.',
'mcp_hero_badge' => '✦ Plus & Pro',
'mcp_hero_h1' => 'Użyj narzędzi DBN z Claude, Cursor i Copilot',
'mcp_hero_sub' => 'Połącz dowolnego klienta MCP ze wszystkimi 19 narzędziami Do Better Norge — transkrypcja, analiza prawna, harmonogramy, redakcja i inne.',
'mcp_token_section_title' => 'Twój token MCP',
'mcp_gate_guest_p' => 'Zaloguj się, aby utworzyć swój osobisty token MCP. Dostępny dla członków Plus i Pro.',
'mcp_gate_guest_btn' => 'Zaloguj się',
'mcp_gate_free_p' => 'Dostęp do MCP jest dostępny w planach Plus i Pro. Uaktualnij, aby połączyć swoje narzędzia AI.',
'mcp_gate_free_btn' => 'Uaktualnij plan',
'mcp_token_hint' => 'Tokeny są wyświetlane tylko raz przy tworzeniu. Utwórz jeden na klienta (Claude, Cursor, VS Code…).',
'mcp_token_create_btn' => 'Utwórz token',
'mcp_token_reveal_label' => 'Skopiuj ten token teraz — nie będzie już wyświetlany:',
'mcp_token_copy_btn' => 'Skopiuj token',
'mcp_token_no_tokens' => 'Brak tokenów MCP jeszcze.',
'mcp_token_active' => 'Aktywny',
'mcp_token_revoked' => 'Cofnięty',
'mcp_token_never_used' => 'Nigdy nie używany',
'mcp_token_last_used' => 'Ostatnio używany',
'mcp_token_revoke_btn' => 'Cofnij',
'mcp_config_title' => 'Konfiguracja klienta',
'mcp_config_hint' => 'Wklej swój token do konfiguracji poniżej po jego utworzeniu powyżej.',
'mcp_config_token_filled' => 'Token automatycznie wypełniony.',
'mcp_config_run_terminal' => 'Uruchom w swoim terminalu:',
'mcp_test_btn' => 'Testuj połączenie',
'mcp_test_no_token' => 'Najpierw utwórz token.',
'mcp_test_testing' => 'Testowanie…',
'mcp_tools_title' => 'Dostępne narzędzia',
'mcp_tools_sub' => 'Wszystkie narzędzia działają na kredytach Twojego planu Plus lub Pro. Kliknij kartę, aby uzyskać pełne szczegóły techniczne.',
'mcp_tools_param_req_hint' => 'Fioletowy = wymagany',
'mcp_tools_view_details' => 'Zobacz szczegóły →',
'mcp_privacy_title' => 'Prywatność',
'mcp_privacy_text' => 'Domyślnie przetwarzaj i zapomnij. Wszystkie wywołania narzędzi przetwarzają Twój tekst w pamięci i zwracają wyniki do Twojego klienta AI. Nic nie jest zapisywane w Mojej Sprawie, chyba że wyraźnie wywołasz dbn.save_to_case.',
'mcp_privacy_legal' => 'Narzędzia zapewniają wsparcie w zakresie przygotowania prawnego, a nie ostatecznej porady prawnej. Wyniki mają charakter informacyjny i powinny być przeglądane przez wykwalifikowanego profesjonalistę prawnego.',
'mcp_tool_back' => '← Powrót do konfiguracji MCP',
'mcp_tool_params_title' => 'Parametry',
'mcp_tool_no_params' => 'To narzędzie nie przyjmuje parametrów wejściowych.',
'mcp_tool_col_param' => 'Parametr',
'mcp_tool_col_type' => 'Typ',
'mcp_tool_col_required' => 'Wymagany',
'mcp_tool_col_desc' => 'Opis',
'mcp_tool_example_req' => 'Przykładowe żądanie',
'mcp_tool_example_resp' => 'Przykładowa odpowiedź',
'mcp_tool_connect_title' => 'Połącz',
'mcp_tool_connect_text' => 'Utwórz swój token MCP na stronie konfiguracji i użyj go z dowolnym obsługiwanym klientem.',
'mcp_tool_setup_link' => 'Skonfiguruj MCP →',
'mcp_tool_yes' => 'Tak',
'mcp_tool_no' => 'Nie',
),
);