<script setup lang="ts">
import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useToast } from 'gnexus-ui-kit/vue'
// Страница-инструкция по MCP (ТЗ 3.10): что реализует, как подключить агента,
// как сервер держит контекст проекта. Ссылка — из «Настроек».
const { t } = useI18n()
const toast = useToast()
// Адрес MCP совпадает с адресом приложения: /mcp монтируется в backend,
// в dev проксируется vite (как /api)
const mcpUrl = computed(() => `${window.location.origin}/mcp/`)
const copied = ref(false)
async function copyUrl() {
try {
await navigator.clipboard.writeText(mcpUrl.value)
copied.value = true
toast.success({ title: t('mcp.copied') })
setTimeout(() => (copied.value = false), 1500)
} catch {
toast.error({ title: t('mcp.copyFailed') })
}
}
// Инструменты сервера: имя + короткое описание (детали агент получает сам —
// из описаний тулов и серверных instructions)
const tools = computed(() => [
{ name: 'list_projects', desc: t('mcp.toolListProjects') },
{ name: 'get_project', desc: t('mcp.toolGetProject') },
{ name: 'create_task', desc: t('mcp.toolCreateTask') },
{ name: 'update_task', desc: t('mcp.toolUpdateTask') },
{ name: 'complete_task', desc: t('mcp.toolCompleteTask') },
{ name: 'list_tasks', desc: t('mcp.toolListTasks') },
{ name: 'get_task', desc: t('mcp.toolGetTask') },
{ name: 'list_tags', desc: t('mcp.toolListTags') },
])
const claudeCommand = computed(
() =>
`claude mcp add --transport http gntodo ${mcpUrl.value} --header "Authorization: Bearer <MCP_TOKEN>"`,
)
const jsonConfig = computed(
() =>
JSON.stringify(
{
mcpServers: {
gntodo: {
type: 'http',
url: mcpUrl.value,
headers: { Authorization: 'Bearer <MCP_TOKEN>' },
},
},
},
null,
2,
),
)
</script>
<template>
<section>
<GnPageHeader kicker="GNexus Tasks" :title="t('mcp.title')" />
<GnCard>
<template #title>{{ t('mcp.whatTitle') }}</template>
<p class="hint">{{ t('mcp.whatText') }}</p>
</GnCard>
<GnCard>
<template #title>{{ t('mcp.toolsTitle') }}</template>
<p class="hint">{{ t('mcp.toolsHint') }}</p>
<div class="tools">
<div v-for="tool in tools" :key="tool.name" class="tool">
<code>{{ tool.name }}</code>
<span>{{ tool.desc }}</span>
</div>
</div>
</GnCard>
<GnCard>
<template #title>{{ t('mcp.connectTitle') }}</template>
<p class="hint">{{ t('mcp.connectText') }}</p>
<div class="field">
<div class="field-label">{{ t('mcp.connectUrlLabel') }}</div>
<div class="code-line">
<code>{{ mcpUrl }}</code>
<GnButton
size="sm"
variant="secondary"
:icon="copied ? 'ph-check' : 'ph-copy'"
@click="copyUrl"
>
{{ t('mcp.copy') }}
</GnButton>
</div>
</div>
<div class="field">
<div class="field-label">{{ t('mcp.connectTokenLabel') }}</div>
<p class="hint">{{ t('mcp.connectTokenText') }}</p>
</div>
<div class="field">
<div class="field-label">{{ t('mcp.connectClaudeLabel') }}</div>
<pre class="code-block"><code>{{ claudeCommand }}</code></pre>
</div>
<div class="field">
<div class="field-label">{{ t('mcp.connectJsonLabel') }}</div>
<pre class="code-block"><code>{{ jsonConfig }}</code></pre>
</div>
</GnCard>
<GnCard>
<template #title>{{ t('mcp.projectTitle') }}</template>
<p class="hint">{{ t('mcp.projectText') }}</p>
</GnCard>
</section>
</template>
<style scoped>
.card + .card,
.card {
margin-top: 1.5rem;
}
.hint {
color: var(--muted, #888);
margin: 0 0 0.75rem;
}
.tools {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.tool {
display: flex;
align-items: baseline;
gap: 0.75rem;
}
.tool code {
flex: 0 0 9.5rem;
font-size: 0.85rem;
}
.tool span {
color: var(--muted, #888);
font-size: 0.9rem;
}
.field {
margin-top: 1rem;
}
.field-label {
font-weight: 600;
margin-bottom: 0.35rem;
}
.code-line {
display: flex;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
}
.code-line code,
.code-block code {
background: var(--surface-alt, rgba(128, 128, 128, 0.12));
border-radius: 6px;
padding: 0.25rem 0.5rem;
font-size: 0.85rem;
word-break: break-all;
}
.code-block {
margin: 0;
overflow-x: auto;
}
.code-block code {
display: block;
padding: 0.6rem 0.75rem;
white-space: pre;
}
</style>