Skip to content

Commit a27e7aa

Browse files
Merge pull request #260 from guillermoscript/feature/mcp
Feature/mcp
2 parents e05d03b + 173e7c1 commit a27e7aa

4 files changed

Lines changed: 49 additions & 7 deletions

File tree

app/[locale]/dashboard/admin/api-tokens/page.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getUserRole } from '@/lib/supabase/get-user-role'
2+
import { getCurrentTenant } from '@/lib/supabase/tenant'
23
import { redirect } from 'next/navigation'
34
import { listMcpTokens } from '@/app/actions/mcp-tokens'
45
import ApiTokensPage from '@/components/dashboard/api-tokens-page'
@@ -10,11 +11,15 @@ export default async function AdminApiTokensPage() {
1011
}
1112

1213
const { data: tokens } = await listMcpTokens()
13-
const domain = process.env.NEXT_PUBLIC_PLATFORM_DOMAIN || 'localhost:3000'
14+
const tenant = await getCurrentTenant()
15+
const platformDomain = process.env.NEXT_PUBLIC_PLATFORM_DOMAIN || 'localhost:3000'
16+
const mcpUrl = tenant?.slug
17+
? `https://${tenant.slug}.${platformDomain}/api/mcp/cli`
18+
: `https://${platformDomain}/api/mcp/cli`
1419

1520
return (
1621
<div className="p-6 lg:p-8">
17-
<ApiTokensPage tokens={tokens ?? []} domain={domain} />
22+
<ApiTokensPage tokens={tokens ?? []} mcpUrl={mcpUrl} />
1823
</div>
1924
)
2025
}

app/[locale]/dashboard/teacher/api-tokens/page.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { getUserRole } from '@/lib/supabase/get-user-role'
2+
import { getCurrentTenant } from '@/lib/supabase/tenant'
23
import { redirect } from 'next/navigation'
34
import { listMcpTokens } from '@/app/actions/mcp-tokens'
45
import ApiTokensPage from '@/components/dashboard/api-tokens-page'
@@ -10,11 +11,15 @@ export default async function TeacherApiTokensPage() {
1011
}
1112

1213
const { data: tokens } = await listMcpTokens()
13-
const domain = process.env.NEXT_PUBLIC_PLATFORM_DOMAIN || 'localhost:3000'
14+
const tenant = await getCurrentTenant()
15+
const platformDomain = process.env.NEXT_PUBLIC_PLATFORM_DOMAIN || 'localhost:3000'
16+
const mcpUrl = tenant?.slug
17+
? `https://${tenant.slug}.${platformDomain}/api/mcp/cli`
18+
: `https://${platformDomain}/api/mcp/cli`
1419

1520
return (
1621
<div className="p-6 lg:p-8">
17-
<ApiTokensPage tokens={tokens ?? []} domain={domain} />
22+
<ApiTokensPage tokens={tokens ?? []} mcpUrl={mcpUrl} />
1823
</div>
1924
)
2025
}

components/dashboard/api-tokens-page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ import { createMcpToken, revokeMcpToken, deleteMcpToken, type McpToken } from '@
2727

2828
interface ApiTokensPageProps {
2929
tokens: McpToken[]
30-
domain: string
30+
mcpUrl: string
3131
}
3232

33-
export default function ApiTokensPage({ tokens, domain }: ApiTokensPageProps) {
33+
export default function ApiTokensPage({ tokens, mcpUrl }: ApiTokensPageProps) {
3434
const t = useTranslations('dashboard.admin.apiTokens')
3535
const [createOpen, setCreateOpen] = useState(false)
3636
const [revealedToken, setRevealedToken] = useState<string | null>(null)
@@ -96,7 +96,7 @@ export default function ApiTokensPage({ tokens, domain }: ApiTokensPageProps) {
9696
const configSnippet = (token: string) => JSON.stringify({
9797
mcpServers: {
9898
lms: {
99-
url: `https://${domain}/api/mcp/cli`,
99+
url: mcpUrl,
100100
headers: {
101101
Authorization: `Bearer ${token}`,
102102
},
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
-- Fix validate_mcp_api_token RPC to use tenant_users (authoritative role source)
2+
-- instead of the legacy user_roles/roles join which had wrong column references
3+
DROP FUNCTION IF EXISTS validate_mcp_api_token(TEXT);
4+
5+
CREATE FUNCTION validate_mcp_api_token(token_input TEXT)
6+
RETURNS TABLE(user_id UUID, email TEXT, user_role TEXT, token_id BIGINT)
7+
LANGUAGE plpgsql
8+
SECURITY DEFINER
9+
AS $$
10+
DECLARE
11+
token_hash_input TEXT;
12+
BEGIN
13+
-- Hash the input token
14+
token_hash_input := encode(digest(token_input, 'sha256'), 'hex');
15+
16+
-- Find matching token and get user info via tenant_users (authoritative role source)
17+
RETURN QUERY
18+
SELECT
19+
t.user_id,
20+
u.email::TEXT,
21+
tu.role::TEXT,
22+
t.id
23+
FROM mcp_api_tokens t
24+
JOIN auth.users u ON u.id = t.user_id
25+
JOIN tenant_users tu ON tu.user_id = t.user_id AND tu.status = 'active'
26+
WHERE t.token_hash = token_hash_input
27+
AND t.is_active = true
28+
AND (t.expires_at IS NULL OR t.expires_at > NOW())
29+
AND tu.role IN ('teacher', 'admin')
30+
LIMIT 1;
31+
END;
32+
$$;

0 commit comments

Comments
 (0)