c997f204b5
- UserMcpTokens: per-user SHA256-hashed token mint/validate/revoke (Plus/Pro only) - DbnMcpRuntime: 19 MCP tools (search, ask, summarize, timeline, redact, translate, legal_analysis, korrespond, barnevernet_analyze, advocate_brief, deep_research, discrepancy_find, transcribe_audio, corpus_stats, list_documents, get_document, citation_graph, case_workbench_plan, save_to_case) - api/mcp/user/: session/tools/invoke HTTP endpoints with Bearer token auth - api/mcp-tokens.php: token create/revoke/list REST API - mcp.php: interactive setup page with token management, 5-client config tabs, auto-fill on token creation, tool catalog grid, privacy notice - account.php: simplified MCP section with link to mcp.php - nav.php: MCP nav link - .htaccess: Authorization header passthrough, MCP route rewrite, CORS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
74 lines
2.6 KiB
PHP
74 lines
2.6 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../../../includes/bootstrap.php';
|
|
require_once __DIR__ . '/../../../includes/DbnMcpRuntime.php';
|
|
|
|
header('Access-Control-Allow-Origin: *');
|
|
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
|
|
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-DBN-MCP-Token');
|
|
|
|
if (($_SERVER['REQUEST_METHOD'] ?? '') === 'OPTIONS') {
|
|
http_response_code(204);
|
|
exit;
|
|
}
|
|
|
|
function dbnMcpUserTokenFromRequest(): string
|
|
{
|
|
$header = (string)($_SERVER['HTTP_AUTHORIZATION'] ?? '');
|
|
if (preg_match('/^Bearer\s+(.+)$/i', $header, $m)) {
|
|
return trim($m[1]);
|
|
}
|
|
return trim((string)($_SERVER['HTTP_X_DBN_MCP_TOKEN'] ?? $_SERVER['HTTP_X_MCP_TOKEN'] ?? ''));
|
|
}
|
|
|
|
function dbnMcpPathSegments(): array
|
|
{
|
|
$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
|
|
$path = preg_replace('#^.*?/api/mcp/user/?#', '', $path);
|
|
$path = trim((string)$path, '/');
|
|
return $path === '' ? [] : explode('/', $path);
|
|
}
|
|
|
|
try {
|
|
$token = dbnMcpUserTokenFromRequest();
|
|
$tokenRow = UserMcpTokens::resolve($token);
|
|
if ($tokenRow === null) {
|
|
dbnToolsError('Invalid, revoked, or non-Plus/Pro DBN MCP token.', 401, 'invalid_token');
|
|
}
|
|
|
|
$segments = dbnMcpPathSegments();
|
|
$resource = $segments[0] ?? 'session';
|
|
$method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
|
|
|
|
if ($resource === 'session' && $method === 'GET') {
|
|
dbnToolsRespond([
|
|
'ok' => true,
|
|
'user' => [
|
|
'id' => (int)$tokenRow['user_id'],
|
|
'email' => (string)($tokenRow['email'] ?? ''),
|
|
'tier' => (string)$tokenRow['tier'],
|
|
],
|
|
'privacy' => 'Tool calls process in memory by default. Use dbn.save_to_case to persist a result.',
|
|
]);
|
|
}
|
|
|
|
if ($resource === 'tools' && $method === 'GET' && !isset($segments[1])) {
|
|
dbnToolsRespond(['ok' => true, 'tools' => DbnMcpRuntime::tools()]);
|
|
}
|
|
|
|
if ($resource === 'tools' && $method === 'POST' && isset($segments[1]) && ($segments[2] ?? '') === 'invoke') {
|
|
$slug = urldecode((string)$segments[1]);
|
|
$args = dbnToolsJsonInput(2_500_000);
|
|
$result = DbnMcpRuntime::invoke($slug, $args, $tokenRow);
|
|
dbnToolsRespond($result);
|
|
}
|
|
|
|
dbnToolsError('Unknown MCP user route.', 404, 'not_found', ['path' => $segments]);
|
|
} catch (DbnToolsHttpException $e) {
|
|
dbnToolsError($e->getMessage(), $e->status, $e->errorCode, $e->extra);
|
|
} catch (Throwable $e) {
|
|
error_log('[dbn-user-mcp] ' . $e->getMessage());
|
|
dbnToolsError('DBN MCP runtime failed.', 500, 'internal_error');
|
|
}
|