Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions app/components/agent/AgentShader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ const props = withDefaults(
{ variant: 'default' }
)

const colorMode = useColorMode()
const isHero = computed(() => props.variant === 'hero')
const isDark = computed(() => colorMode.value === 'dark')

const logoTransform = computed(() =>
isHero.value
Expand All @@ -19,8 +21,13 @@ const logoTransform = computed(() =>
)

const dotDensity = computed(() => (isHero.value ? 36 : 30))
const dotSize = computed(() => (isHero.value ? 0.4 : 0.2))
const dotSize = computed(() =>
isDark.value
? (isHero.value ? 0.4 : 0.2)
: (isHero.value ? 0.45 : 0.26)
)
const twinkle = computed(() => (isHero.value ? 2 : 1))
const dotColor = computed(() => (isDark.value ? '#4cffa8' : '#00945E'))
</script>

<template>
Expand All @@ -37,7 +44,7 @@ const twinkle = computed(() => (isHero.value ? 2 : 1))
:transform="logoTransform"
/>
<DotGrid
color="#4cffa8"
:color="dotColor"
:density="dotDensity"
:dot-size="dotSize"
:twinkle="twinkle"
Expand Down
23 changes: 7 additions & 16 deletions server/api/agent.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { ToolSet, UIMessage } from 'ai'
import { createMCPClient } from '@ai-sdk/mcp'
import { anthropic } from '@ai-sdk/anthropic'
import { createAILogger, createEvlogIntegration } from 'evlog/ai'
import type { AILogger } from 'evlog/ai'
import { sql } from 'drizzle-orm'
import { getAgentFingerprint } from '../utils/agent-fingerprint'
import { showModuleTool } from '../utils/tools/show-module'
Expand Down Expand Up @@ -91,15 +90,6 @@ function buildSystemPrompt(pagePath: string | null): string {
return `Current page: ${pagePath}\n\n${withDate}`
}

function computeEstimatedCost(state: AILogger['_state']): number {
if (!state.costMap) return 0
const model = state.models.at(-1)
if (!model) return 0
const cost = state.costMap[model]
if (!cost) return 0
return (state.usage.inputTokens * cost.input + state.usage.outputTokens * cost.output) / 1_000_000
}

export default defineEventHandler(async (event) => {
const raw = await readBody(event) as { messages?: unknown } | null
if (!raw || !Array.isArray(raw.messages)) {
Expand Down Expand Up @@ -142,12 +132,13 @@ export default defineEventHandler(async (event) => {
if (!chatId) return
const fingerprint = await getAgentFingerprint(event)
const now = new Date()
const state = ai._state
const model = state.models.at(-1) ?? null
const provider = state.lastProvider ?? null
const { inputTokens, outputTokens } = state.usage
const estimatedCost = computeEstimatedCost(state)
const durationMs = state.totalDurationMs ?? 0
const metadata = ai.getMetadata()
const model = metadata.model ?? null
const provider = metadata.provider ?? null
const inputTokens = metadata.inputTokens ?? 0
const outputTokens = metadata.outputTokens ?? 0
const estimatedCost = metadata.estimatedCost ?? 0
const durationMs = metadata.totalDurationMs ?? 0

// Insert when chatId is new; on conflict only update when the existing row's
// fingerprint matches — prevents anyone with a guessable chatId from
Expand Down
34 changes: 10 additions & 24 deletions server/routes/.well-known/mcp/server-card.json.get.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
export default defineCachedEventHandler((event) => {
import { listMcpDefinitions } from '@nuxtjs/mcp-toolkit/server'

export default defineCachedEventHandler(async (event) => {
const domain = getSiteUrl(event)
const { tools, resources, prompts } = await listMcpDefinitions()

const isPublic = <T extends { group?: string }>(d: T) => d.group !== 'admin'

const serverCard = {
$schema: 'https://modelcontextprotocol.io/schema/server-card/v1',
serverInfo: {
Expand All @@ -17,35 +23,15 @@ export default defineCachedEventHandler((event) => {
url: `${domain}/mcp`
}
],
// TODO: import these from `@nuxtjs/mcp-toolkit` so we don't duplicate the tool/resource/prompt definitions.
capabilities: {
tools: { listChanged: false },
resources: { listChanged: false, subscribe: false },
prompts: { listChanged: false },
logging: {}
},
tools: [
{ name: 'list-documentation-pages', description: 'List all available Nuxt documentation pages with their categories and basic information.' },
{ name: 'get-documentation-page', description: 'Retrieve the full content and details of a specific Nuxt documentation page.' },
{ name: 'get-getting-started-guide', description: 'Get the getting started guide for a specific Nuxt version.' },
{ name: 'list-modules', description: 'List all available Nuxt modules with optional filtering and sorting capabilities.' },
{ name: 'get-module', description: 'Retrieve complete details about a specific Nuxt module including README, compatibility, maintainers, and stats.' },
{ name: 'list-blog-posts', description: 'List all Nuxt blog posts with metadata including titles, dates, categories, tags, and descriptions.' },
{ name: 'get-blog-post', description: 'Retrieve the full content and details of a specific Nuxt blog post.' },
{ name: 'list-deploy-providers', description: 'List all deployment providers and hosting platforms for Nuxt applications with their features and capabilities.' },
{ name: 'get-deploy-provider', description: 'Retrieve detailed deployment instructions and setup guide for a specific hosting provider.' },
{ name: 'get-changelog', description: 'Retrieve the latest releases from Nuxt core and official modules (changelog).' }
],
resources: [
{ name: 'documentation-pages', description: 'Catalog of all Nuxt documentation pages.' },
{ name: 'blog-posts', description: 'Catalog of all Nuxt blog posts.' },
{ name: 'deploy-providers', description: 'Catalog of all Nuxt deployment providers.' }
],
prompts: [
{ name: 'find-documentation-for-topic', description: 'Find the best Nuxt documentation for a specific topic or feature.' },
{ name: 'deployment-guide', description: 'Get deployment instructions for a specific hosting provider.' },
{ name: 'migration-help', description: 'Get help with migrating between Nuxt versions.' }
],
tools: tools.filter(isPublic).map(t => ({ name: t.name, description: t.description })),
resources: resources.filter(isPublic).map(r => ({ name: r.name, uri: r.uri, description: r.description })),
prompts: prompts.filter(isPublic).map(p => ({ name: p.name, description: p.description })),
authentication: {
required: false
}
Expand Down