Compare commits

..

16 Commits

Author SHA1 Message Date
2a2e32c57b fix: corriger la structure de configuration de l'interface utilisateur dans app.config.ts 2025-12-19 17:06:33 +01:00
6ba90a877e chore: update dependencies for @nuxt/ui, @nuxthub/core, vue 2025-12-19 17:04:34 +01:00
66bec24839 refactor: améliorer la mise en page et la structure des composants sur la page d'accueil 2025-12-19 17:02:19 +01:00
d5955d3824 feat: update package.json 2025-12-19 16:54:28 +01:00
33651945b8 refactor: transition from MCP to REST API for portfolio data
- Removed MCP server integration and related components from index.vue and contact.json.
- Updated project documentation to reflect the new ArtAPI server, including changes to project metadata and descriptions.
- Eliminated MCP toolkit dependencies from nuxt.config.ts and package.json.
- Introduced new REST API endpoints for chat prompts, replacing previous MCP prompts.
- Deleted obsolete MCP resources and prompts, streamlining the codebase for a more focused API approach.
2025-12-19 16:48:42 +01:00
30dedc2768 fix: supprimer les variables NUXT_DISCORD_ID et NUXT_DISCORD_TOKEN du workflow Cloudflare 2025-12-19 16:31:57 +01:00
a7a581a97d fix: mettre à jour les types générés par Wrangler pour refléter les modifications récentes 2025-12-19 16:31:42 +01:00
f63d732244 Refactor code structure for improved readability and maintainability 2025-12-19 16:31:10 +01:00
0295a49f70 feat: ajouter un outil pour lire des ressources depuis l'API serveur 2025-12-19 16:25:51 +01:00
71ed829f38 fix: mettre à jour les couleurs de fond et de carte pour un meilleur contraste en mode sombre 2025-12-19 16:25:51 +01:00
e9e639a775 feat: ajouter le fichier de configuration wrangler.jsonc et mettre à jour .gitignore 2025-12-19 16:25:44 +01:00
b431d5b02b feat: ajouter la dépendance better-sqlite3 et mettre à jour les versions des paquets 2025-12-18 23:10:57 +01:00
83bbdbd59e Merge pull request #9 from ArthurDanjou/copilot/fix-ucard-color-in-darkmode
Fix UCard code block contrast in dark mode and add color mode selector
2025-12-18 23:10:04 +01:00
copilot-swe-agent[bot]
10d6dfa741 Fix UCard dark mode styling and add ColorModeSelect component
Co-authored-by: ArthurDanjou <29738535+ArthurDanjou@users.noreply.github.com>
2025-12-18 22:04:20 +00:00
copilot-swe-agent[bot]
9ea985c5c2 Add better-sqlite3 dependency for Nuxt Content
Co-authored-by: ArthurDanjou <29738535+ArthurDanjou@users.noreply.github.com>
2025-12-18 21:59:18 +00:00
copilot-swe-agent[bot]
ff6cae2198 Initial plan 2025-12-18 21:54:15 +00:00
36 changed files with 559 additions and 1186 deletions

View File

@@ -1,5 +1,3 @@
NUXT_DISCORD_ID=
NUXT_DISCORD_TOKEN=
NUXT_DISCORD_USER_ID=
NUXT_WAKATIME_CODING=

View File

@@ -25,8 +25,6 @@ jobs:
- name: Build
run: bun run build
env:
NUXT_DISCORD_ID: ${{ vars.NUXT_DISCORD_ID }}
NUXT_DISCORD_TOKEN: ${{ vars.NUXT_DISCORD_TOKEN }}
NUXT_DISCORD_USER_ID: ${{ vars.NUXT_DISCORD_USER_ID }}
NUXT_WAKATIME_CODING: ${{ vars.NUXT_WAKATIME_CODING }}

3
.gitignore vendored
View File

@@ -24,5 +24,4 @@ logs
.env.*
!.env.example
.wrangler
wrangler.jsonc
.wrangler

View File

@@ -1,5 +1,8 @@
export default defineAppConfig({
ui: {
container: {
base: 'max-w-6xl'
},
colors: {
primary: 'blue',
secondary: 'purple',

View File

@@ -1,5 +1,10 @@
<template>
<NuxtRouteAnnouncer />
<NuxtLoadingIndicator />
<NuxtPage />
<div>
<NuxtRouteAnnouncer />
<NuxtLoadingIndicator />
<div class="fixed top-4 right-4 z-50">
<UColorModeSelect />
</div>
<NuxtPage />
</div>
</template>

View File

@@ -1,419 +0,0 @@
<script setup lang="ts">
const tools = [
{
name: 'activity',
description: 'Real-time current activity and status of Arthur Danjou, including what he\'s currently working on.',
inputs: []
},
{
name: 'resume-link',
description: 'Retrieves a direct download link to Arthur Danjou\'s professional resume in the specified language.',
inputs: [{ name: 'lang', type: 'string', description: 'The language for the resume: \'en\' for English or \'fr\' for French' }]
},
{
name: 'stats',
description: 'Detailed coding statistics and analytics from WakaTime, including programming languages, time spent coding, and productivity metrics.',
inputs: []
},
{
name: 'status-page',
description: 'Real-time status, uptime monitoring, and incident reports for Arthur Danjou\'s homelab infrastructure, powered by UptimeKuma.',
inputs: []
},
{
name: 'uses-by-category',
description: 'Retrieves a filtered list of tools, software, and hardware used by Arthur Danjou based on a specific category.',
inputs: [{ name: 'categoryName', type: 'string', description: 'The category to filter by: \'homelab\', \'ide\', \'hardware\', or \'software\'' }]
},
{
name: 'weather',
description: 'Get current weather for a city.',
inputs: [{ name: 'city', type: 'string', description: 'City name' }]
}
]
const prompts = [
{ name: 'activity', description: 'Retrieve Arthur Danjou\'s current real-time activity status' },
{ name: 'contact', description: 'Retrieve contact information and social media links' },
{ name: 'hobbies', description: 'Retrieve information about hobbies and interests' },
{ name: 'languages', description: 'Retrieve languages spoken with proficiency levels' },
{ name: 'profile', description: 'Retrieve comprehensive professional profile information' },
{ name: 'projects', description: 'Retrieve list of personal and professional projects' },
{ name: 'resume', description: 'Request and retrieve professional resume in specified language' },
{ name: 'skills', description: 'Retrieve comprehensive list of technical skills' },
{ name: 'stats', description: 'Retrieve detailed coding statistics from WakaTime' },
{ name: 'status-page', description: 'Retrieve real-time status page of homelab infrastructure' },
{ name: 'uses-by-category', description: 'Retrieve tools, software, and hardware by category' }
]
const resources = [
{ uri: 'resource://artmcp/profile', title: 'Professional Profile', description: 'Biography, location, availability, career goals' },
{ uri: 'resource://artmcp/contact', title: 'Contact Information', description: 'Email, LinkedIn, GitHub, social media links' },
{ uri: 'resource://artmcp/education', title: 'Education', description: 'Degrees, institutions, academic achievements' },
{ uri: 'resource://artmcp/experiences', title: 'Experiences', description: 'Professional work experiences and roles' },
{ uri: 'resource://artmcp/hobbies', title: 'Hobbies & Interests', description: 'Personal hobbies and passions' },
{ uri: 'resource://artmcp/languages', title: 'Spoken Languages', description: 'Languages with proficiency levels' },
{ uri: 'resource://artmcp/projects', title: 'Projects Portfolio', description: 'Technical projects and achievements' },
{ uri: 'resource://artmcp/skills', title: 'Skills', description: 'Technical skills and tools mastered' },
{ uri: 'resource://artmcp/uses', title: 'Tech Stack & Tools', description: 'Tools, software, and hardware used' }
]
const apiEndpoints = [
{ method: 'GET', path: '/api/profile', description: 'Profile information' },
{ method: 'GET', path: '/api/contact', description: 'Contact information' },
{ method: 'GET', path: '/api/skills', description: 'Technical skills' },
{ method: 'GET', path: '/api/experiences', description: 'Work experiences' },
{ method: 'GET', path: '/api/education', description: 'Education background' },
{ method: 'GET', path: '/api/projects', description: 'Projects portfolio' },
{ method: 'GET', path: '/api/languages', description: 'Spoken languages' },
{ method: 'GET', path: '/api/hobbies', description: 'Hobbies and interests' },
{ method: 'GET', path: '/api/uses', description: 'Tech stack and tools' },
{ method: 'GET', path: '/api/activity', description: 'Real-time activity' },
{ method: 'GET', path: '/api/stats', description: 'Coding statistics' },
{ method: 'GET', path: '/api/status-page', description: 'Status page' },
{ method: 'GET', path: '/api/resumes/{en|fr}', description: 'Download resume' }
]
</script>
<template>
<div class="min-h-screen bg-gray-50 dark:bg-gray-900">
<!-- Hero Header -->
<div class="bg-linear-to-br from-primary to-primary-600 dark:from-primary-900 dark:to-primary-800">
<UContainer class="py-16">
<div class="text-center text-white">
<UBadge
color="neutral"
variant="subtle"
size="lg"
class="mb-4"
>
Model Context Protocol
</UBadge>
<h1 class="text-4xl sm:text-5xl font-bold mb-4">
ArtMCP Documentation
</h1>
<p class="text-xl text-white/90 mb-8 max-w-2xl mx-auto">
Complete documentation for ArtMCP - Arthur Danjou's Model Context Protocol Server
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<UButton
to="/"
size="lg"
color="neutral"
variant="solid"
icon="i-heroicons-arrow-left"
>
Back to Home
</UButton>
<UButton
to="https://github.com/ArthurDanjou/artapi"
target="_blank"
size="lg"
color="neutral"
variant="outline"
icon="i-simple-icons-github"
>
View on GitHub
</UButton>
</div>
</div>
</UContainer>
</div>
<!-- Main Content -->
<UContainer class="py-12">
<!-- Overview -->
<UCard
class="mb-8"
:ui="{ body: 'p-8' }"
variant="outline"
>
<h2 class="text-3xl font-bold mb-4">
What is ArtMCP?
</h2>
<p class="text-lg text-gray-600 dark:text-gray-400 mb-4">
ArtMCP provides AI assistants and applications with structured access to Arthur Danjou's professional information through the Model Context Protocol. This includes:
</p>
<div class="grid md:grid-cols-2 gap-4">
<div class="flex items-start gap-3">
<UIcon
name="i-heroicons-check-circle"
class="w-6 h-6 text-green-500 shrink-0 mt-0.5"
/>
<div>
<p class="font-medium">
Comprehensive profile data and biography
</p>
</div>
</div>
<div class="flex items-start gap-3">
<UIcon
name="i-heroicons-check-circle"
class="w-6 h-6 text-green-500 shrink-0 mt-0.5"
/>
<div>
<p class="font-medium">
Work experiences and professional background
</p>
</div>
</div>
<div class="flex items-start gap-3">
<UIcon
name="i-heroicons-check-circle"
class="w-6 h-6 text-green-500 shrink-0 mt-0.5"
/>
<div>
<p class="font-medium">
Portfolio of projects and achievements
</p>
</div>
</div>
<div class="flex items-start gap-3">
<UIcon
name="i-heroicons-check-circle"
class="w-6 h-6 text-green-500 shrink-0 mt-0.5"
/>
<div>
<p class="font-medium">
Technical skills and expertise
</p>
</div>
</div>
<div class="flex items-start gap-3">
<UIcon
name="i-heroicons-check-circle"
class="w-6 h-6 text-green-500 shrink-0 mt-0.5"
/>
<div>
<p class="font-medium">
Education and academic background
</p>
</div>
</div>
<div class="flex items-start gap-3">
<UIcon
name="i-heroicons-check-circle"
class="w-6 h-6 text-green-500 shrink-0 mt-0.5"
/>
<div>
<p class="font-medium">
Real-time activity and coding statistics
</p>
</div>
</div>
</div>
</UCard>
<!-- Quick Start -->
<UCard
class="mb-8"
:ui="{ body: 'p-8' }"
variant="outline"
>
<h2 class="text-3xl font-bold mb-6">
Quick Start
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-6">
Get started with ArtMCP in seconds by installing it in your favorite AI assistant:
</p>
<div class="grid md:grid-cols-2 gap-6">
<div>
<h3 class="text-xl font-semibold mb-4">
MCP Configuration
</h3>
<UCard variant="solid">
<pre class="text-sm text-gray-100 overflow-x-auto">
<code>{
"mcpServers": {
"artmcp": {
"url": "https://api.arthurdanjou.fr/mcp"
}
}
}</code>
</pre>
</UCard>
</div>
<div>
<h3 class="text-xl font-semibold mb-4">
Direct Connection
</h3>
<UCard variant="solid">
<pre class="text-sm text-gray-100 overflow-x-auto"><code>https://api.arthurdanjou.fr/mcp</code></pre>
</UCard>
<p class="text-sm text-gray-600 dark:text-gray-400 mt-2">
Use this URL to connect your MCP client directly
</p>
</div>
</div>
</UCard>
<!-- Tools Section -->
<UCard
class="mb-8"
:ui="{ body: 'p-8' }"
variant="outline"
>
<h2 class="text-3xl font-bold mb-4">
Tools
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-6">
Executable functions that AI assistants can call to perform actions or retrieve dynamic data.
</p>
<UAccordion
:items="tools.map(tool => ({
label: tool.name,
icon: 'i-heroicons-wrench-screwdriver',
defaultOpen: false,
content: tool.description,
slot: 'tool-' + tool.name
}))"
>
<template
v-for="tool in tools"
:key="tool.name"
#[`tool-${tool.name}`]
>
<div class="p-4">
<p class="text-gray-700 dark:text-gray-300 mb-4">
{{ tool.description }}
</p>
<div
v-if="tool.inputs.length > 0"
class="border-t border-gray-200 dark:border-gray-700 pt-4"
>
<h4 class="font-semibold mb-2">
Inputs:
</h4>
<ul class="space-y-2">
<li
v-for="input in tool.inputs"
:key="input.name"
class="flex gap-2"
>
<UBadge
color="primary"
variant="subtle"
>
{{ input.name }}
</UBadge>
<span class="text-sm text-gray-600 dark:text-gray-400">{{ input.description }}</span>
</li>
</ul>
</div>
</div>
</template>
</UAccordion>
</UCard>
<!-- Prompts Section -->
<UCard
class="mb-8"
:ui="{ body: 'p-8' }"
variant="outline"
>
<h2 class="text-3xl font-bold mb-4">
Prompts
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-6">
Pre-configured conversation starters that guide AI assistants on how to query specific information.
</p>
<div class="grid md:grid-cols-2 gap-4">
<UCard
v-for="prompt in prompts"
:key="prompt.name"
:ui="{ body: 'p-4' }"
variant="outline"
>
<div class="flex items-start gap-3">
<UIcon
name="i-heroicons-chat-bubble-left-right"
class="w-5 h-5 text-primary shrink-0 mt-0.5"
/>
<div>
<h3 class="font-semibold mb-1">
{{ prompt.name }}
</h3>
<p class="text-sm text-gray-600 dark:text-gray-400">
{{ prompt.description }}
</p>
</div>
</div>
</UCard>
</div>
</UCard>
<!-- Resources Section -->
<UCard
class="mb-8"
:ui="{ body: 'p-8' }"
variant="outline"
>
<h2 class="text-3xl font-bold mb-4">
Resources
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-6">
Static or semi-static data endpoints that provide structured information.
</p>
<div class="space-y-3">
<UCard
v-for="resource in resources"
:key="resource.uri"
:ui="{ body: 'p-4' }"
variant="outline"
>
<div class="flex flex-col gap-2">
<div class="flex items-center gap-2">
<UIcon
name="i-heroicons-document-text"
class="w-5 h-5 text-primary"
/>
<h3 class="font-semibold">
{{ resource.title }}
</h3>
</div>
<code class="text-xs text-gray-600 dark:text-gray-400 bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
{{ resource.uri }}
</code>
<p class="text-sm text-gray-600 dark:text-gray-400">
{{ resource.description }}
</p>
</div>
</UCard>
</div>
</UCard>
<!-- API Endpoints Section -->
<UCard :ui="{ body: 'p-8' }">
<h2 class="text-3xl font-bold mb-4">
REST API Endpoints
</h2>
<p class="text-gray-600 dark:text-gray-400 mb-6">
All resources are also available as REST API endpoints for direct HTTP access.
</p>
<div class="space-y-2">
<div
v-for="endpoint in apiEndpoints"
:key="endpoint.path"
class="flex items-center gap-3 p-3 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
>
<UBadge
color="success"
variant="subtle"
class="font-mono text-xs"
>
{{ endpoint.method }}
</UBadge>
<code class="text-sm font-mono text-gray-900 dark:text-gray-100 flex-1">{{ endpoint.path }}</code>
<span class="text-sm text-gray-600 dark:text-gray-400">{{ endpoint.description }}</span>
</div>
</div>
</UCard>
</UContainer>
</div>
</template>

View File

@@ -1,10 +1,5 @@
<script setup lang="ts">
const features = [
{
icon: 'i-heroicons-cpu-chip',
title: 'MCP Server Integration',
description: 'Connect AI assistants directly through the Model Context Protocol for real-time profile access.'
},
{
icon: 'i-heroicons-code-bracket',
title: 'REST API Endpoints',
@@ -43,7 +38,7 @@ const resources = [
</script>
<template>
<div class="min-h-screen bg-linear-to-b from-gray-50 to-white dark:from-gray-950 dark:to-gray-900">
<div class="min-h-screen bg-linear-to-b from-gray-50 to-white dark:from-neutral-950 dark:to-neutral-900">
<!-- Hero Section -->
<UContainer class="py-16 sm:py-24">
<div class="text-center">
@@ -53,7 +48,7 @@ const resources = [
size="lg"
class="mb-6"
>
Professional API & MCP Server
Professional API provider for my portfolio
</UBadge>
<h1 class="text-5xl sm:text-6xl font-bold text-gray-900 dark:text-white mb-6">
@@ -70,14 +65,6 @@ const resources = [
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center mb-12">
<UButton
to="/docs"
size="xl"
icon="i-heroicons-book-open"
class="shadow-lg"
>
MCP Documentation
</UButton>
<UButton
to="https://github.com/ArthurDanjou/artapi"
target="_blank"
@@ -97,12 +84,6 @@ const resources = [
>
Nuxt 4
</UBadge>
<UBadge
color="error"
variant="subtle"
>
MCP Protocol
</UBadge>
<UBadge
color="primary"
variant="subtle"
@@ -130,7 +111,7 @@ const resources = [
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="flex flex-wrap justify-center gap-6">
<UCard
v-for="feature in features"
:key="feature.title"
@@ -138,6 +119,7 @@ const resources = [
body: 'p-6'
}"
variant="subtle"
class="w-full md:w-[calc(50%-12px)] lg:w-[calc(50%-12px)]"
>
<div class="flex flex-col items-start gap-4">
<div class="p-3 rounded-lg bg-primary/10">
@@ -181,7 +163,7 @@ const resources = [
size="lg"
block
:ui="{
base: 'hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors duration-200'
base: 'hover:bg-neutral-50 dark:hover:bg-neutral-800 transition-colors duration-200'
}"
>
<div class="flex flex-col items-center gap-2">
@@ -196,45 +178,26 @@ const resources = [
</UContainer>
<!-- Getting Started -->
<UContainer class="py-16">
<UContainer class="py-16 w-full sm:w-3/4 lg:w-1/2 mx-auto">
<UCard
:ui="{
body: 'p-8 sm:p-12'
}"
variant="outline"
>
<div class="grid md:grid-cols-2 gap-8">
<div>
<h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
For AI Assistants (MCP)
</h3>
<p class="text-gray-600 dark:text-gray-400 mb-4">
Configure your MCP client to connect to my server:
</p>
<UCard
variant="solid"
>
<pre class="text-sm text-gray-100 overflow-x-auto">
<code>
{
"mcpServers": {
"artmcp": {
"url": "https://api.arthurdanjou.fr/mcp"
}
}
}</code>
</pre>
</UCard>
</div>
<div>
<div class="flex gap-8">
<div class="w-full">
<h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-4">
For Developers (REST API)
</h3>
<p class="text-gray-600 dark:text-gray-400 mb-4">
Use REST API endpoints in your applications:
</p>
<UCard variant="solid">
<UCard
:ui="{
body: 'bg-neutral-900 dark:bg-neutral-950'
}"
>
<pre class="text-sm text-gray-100 overflow-x-auto">
<code># Get profile information
curl https://api.arthurdanjou.fr/api/profile
@@ -254,18 +217,7 @@ curl https://api.arthurdanjou.fr/api/skills</code>
<h2 class="text-3xl font-bold text-gray-900 dark:text-white mb-4">
Ready to Get Started?
</h2>
<p class="text-lg text-gray-600 dark:text-gray-400 mb-8">
Explore the full documentation or try the API now
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<UButton
to="/docs"
size="xl"
icon="i-heroicons-arrow-right"
trailing
>
Read Documentation
</UButton>
<UButton
to="/api/profile"
target="_blank"

568
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -36,11 +36,6 @@
"icon": "i-ph:globe-duotone",
"value": "https://arthurdanjou.fr"
},
{
"name": "MCP Server",
"icon": "i-ph:globe-duotone",
"value": "https://api.arthurdanjou.fr/mcp"
},
{
"name": "Status Page",
"icon": "i-ph:fire-duotone",

View File

@@ -1,126 +1,31 @@
---
slug: artmcp
title: 🤖 ArtMcp
description: A comprehensive Model Context Protocol (MCP) server exposing professional profile information about Arthur Danjou.
slug: artapi
title: 🤖 ArtAPI
description: Arthur Danjou's Professional API Server providing REST endpoints for portfolio data.
publishedAt: 2025/10/27
readingTime: 3
favorite: true
tags:
- web
- nuxt
- mcp
- chat
---
🤖 [ArtMcp](https://github.com/arthurdanjou/artmcp) - Arthur Danjou's MCP Server
🤖 [ArtAPI](https://github.com/arthurdanjou/artapi) - Arthur Danjou's Professional API Server
A comprehensive [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server exposing professional profile information about Arthur Danjou. Built with [Nuxt](https://nuxt.com) and deployed on [NuxtHub](https://hub.nuxt.com) at the Edge.
**ArtAPI** is a professional API server built with Nuxt 4 and Nitro, providing REST endpoints for Arthur Danjou's portfolio data. It serves as a backend for various applications, including AI assistants, personal projects, and integrations.
🔗 **Live Server**: https://api.arthurdanjou.fr/mcp
## 🎯 Features
### MCP Resources
The server exposes the following resources through the Model Context Protocol:
- **📊 Skills** (`resource://artmcp/skills`) - Complete list of technical skills (programming languages, frameworks, tools)
- **💼 Experiences** (`resource://artmcp/experiences`) - Professional work experience and projects
- **🚀 Projects** (`resource://artmcp/projects`) - Portfolio of personal and professional projects
- **🎓 Education** (`resource://artmcp/education`) - Academic background and degrees
- **🌐 Languages** (`resource://artmcp/languages`) - Spoken languages with proficiency levels
- **👤 Profile** (`resource://artmcp/profile`) - Comprehensive profile with bio, location, availability, career goals, and work preferences
- **🎨 Hobbies** (`resource://artmcp/hobbies`) - Personal interests and activities
- **📞 Contact** (`resource://artmcp/contact`) - Professional contact information and social links
- **🛠️ Uses** (`resource://artmcp/uses`) - Tools, hardware, and software setup
### MCP Tools
- **`activity`** - Real-time current activity and status of Arthur Danjou
- **`resume-link`** - Get download link for resume in English or French
- **`stats`** - Detailed coding statistics and analytics from WakaTime
- **`status-page`** - Real-time status and uptime monitoring for homelab infrastructure
- **`uses-by-category`** - Filter uses by category (homelab, ide, hardware, software)
- **`weather`** - Get current weather for a city
### MCP Prompts
Pre-configured prompts for common queries about:
- Resume generation
- Skills and expertise
- Projects showcase
- Real-time activity
- Contact information
- And more...
🔗 **Live Server**: https://api.arthurdanjou.fr/api
## 🏗️ Architecture
This project uses:
- **Nuxt 4** with Nitro for server-side rendering
- **@nuxt/content** for content management
- **@nuxtjs/mcp-toolkit** for MCP server implementation
- **NuxtHub** for edge deployment on Cloudflare Workers
- **nuxt-studio** for content management studio
- **Zod** for schema validation
## 🚀 Getting Started
### Prerequisites
- Node.js 18+ or Bun
- pnpm 10.12.1+
### Installation
```bash
# Install dependencies
pnpm install
```
### Environment Variables
Create a `.env` file (optional):
```bash
# Discord integration (optional)
NUXT_DISCORD_USER_ID=""
NUXT_DISCORD_ID=""
NUXT_DISCORD_TOKEN=""
# Wakatime integration (optional)
NUXT_WAKATIME_USER_ID=""
NUXT_WAKATIME_CODING=""
NUXT_WAKATIME_EDITORS=""
NUXT_WAKATIME_LANGUAGES=""
NUXT_WAKATIME_OS=""
# Status page (optional)
NUXT_STATUS_PAGE=""
```
### Development
Start the development server on `http://localhost:3000`:
```bash
pnpm dev
```
### Production
Build the application for production:
```bash
pnpm build
```
### Deployment
Deploy to NuxtHub/Cloudflare:
```bash
pnpm deploy
```
## 📚 API Endpoints
All resources are also available as REST API endpoints:
@@ -139,20 +44,6 @@ All resources are also available as REST API endpoints:
- `GET /api/status-page`
- `GET /api/resumes/{en|fr}` - Download resume
## 🧪 Development
### Linting
```bash
pnpm lint
```
### Type Checking
```bash
npx tsc --noEmit --skipLibCheck
```
## 📂 Content Structure
Content is managed in the `content/` directory:
@@ -164,40 +55,12 @@ content/
├── profile.md # Comprehensive profile info
├── contact.json # Contact information
├── hobbies.md # Personal interests
├── documentation.md # MCP documentation
├── experiences/*.md # Work experiences
├── projects/*.md # Project portfolio
├── education/*.md # Academic background
└── uses/*.md # Tools and setup
```
## 🔧 Technologies
- **Frontend/Backend**: Nuxt 4, Vue 3, Nitro
- **MCP**: @nuxtjs/mcp-toolkit
- **Content**: Nuxt Content with better-sqlite3
- **Content Studio**: nuxt-studio
- **Deployment**: Cloudflare Workers via NuxtHub
- **Validation**: Zod schemas
## 📖 MCP Integration
To use this server with an MCP client:
1. Configure your MCP client to connect to `https://api.arthurdanjou.fr/mcp/mcp`
2. Or use the API directly via REST endpoints
Example MCP client configuration:
```json
{
"mcpServers": {
"artmcp": {
"url": "https://api.arthurdanjou.fr/mcp/mcp"
}
}
}
```
## 🤝 Contributing
This is a personal portfolio project. Feel free to use it as inspiration for your own MCP server!

View File

@@ -5,7 +5,6 @@ export default defineNuxtConfig({
'@nuxt/eslint',
'@nuxt/ui',
'@nuxt/content',
'@nuxtjs/mcp-toolkit',
'nuxt-studio'
],
@@ -33,8 +32,7 @@ export default defineNuxtConfig({
runtimeConfig: {
discord: {
userId: '',
id: '',
token: ''
id: ''
},
statusPage: '',
wakatime: {
@@ -69,12 +67,6 @@ export default defineNuxtConfig({
}
},
mcp: {
name: 'ArtMCP',
version: '1.0.0',
browserRedirect: '/docs'
},
studio: {
route: '/studio',
repository: {

View File

@@ -14,14 +14,14 @@
"dependencies": {
"@nuxt/content": "3.9.0",
"@nuxt/eslint": "1.12.1",
"@nuxt/ui": "^4.2.1",
"@nuxthub/core": "0.10.2",
"@nuxtjs/mcp-toolkit": "0.5.2",
"@nuxt/ui": "4.3.0",
"@nuxthub/core": "0.10.3",
"better-sqlite3": "^12.5.0",
"drizzle-kit": "^0.31.8",
"drizzle-orm": "^0.45.1",
"nuxt": "4.2.2",
"nuxt-studio": "1.0.0-alpha.4",
"vue": "3.5.25",
"vue": "3.5.26",
"vue-router": "4.6.4",
"zod": "4.2.1"
},
@@ -33,6 +33,6 @@
"eslint": "9.39.2",
"typescript": "5.9.3",
"vue-tsc": "3.1.8",
"wrangler": "4.55.0"
"wrangler": "4.56.0"
}
}

View File

@@ -0,0 +1,71 @@
interface Prompt {
title: string
description: string
text: string
}
export default defineCachedEventHandler(async () => {
const prompts: Prompt[] = [
{
title: 'Real-time Activity Status',
description: 'Generates a prompt to retrieve Arthur Danjou\'s current real-time activity status, including what he is currently working on.',
text: `Provide me the realtime activity of Arthur Danjou.`
},
{
title: 'Contact Information and Social Links',
description: 'Generates a prompt to retrieve Arthur Danjou\'s contact information and social media links, including email, LinkedIn, GitHub, Twitter, Discord, and personal websites.',
text: `How can I contact Arthur Danjou? Provide all contact methods and social links.`
},
{
title: 'Hobbies and Interests',
description: 'Generates a prompt to retrieve information about Arthur Danjou\'s personal hobbies, interests, and passions outside of professional work.',
text: `What are the hobbies, interests and passions of Arthur Danjou?`
},
{
title: 'Languages and Proficiency Levels',
description: 'Generates a prompt to retrieve the languages spoken by Arthur Danjou along with detailed proficiency levels for each language.',
text: `What languages does Arthur Danjou speak and at what proficiency level?`
},
{
title: 'Comprehensive Professional Profile',
description: 'Generates a prompt to retrieve comprehensive professional profile information about Arthur Danjou, including biography, location, availability status, career goals, and work preferences.',
text: `Provide me comprehensive profile information about Arthur Danjou including his bio, location, availability, career goals, and work preferences.`
},
{
description: 'Generates a prompt to retrieve a comprehensive list of personal and professional projects developed by Arthur Danjou, showcasing his technical skills and achievements.',
title: 'Projects',
text: `Provide me a list of projects done by Arthur Danjou.`
},
{
description: 'Generates a prompt to request and retrieve Arthur Danjou\'s professional resume in the specified language (English or French).',
title: 'Resume',
text: `Provide me the link to download Arthur Danjou's resume in {lang}.`
},
{
description: 'Generates a prompt to retrieve a comprehensive list of technical skills, programming languages, frameworks, and tools mastered by Arthur Danjou.',
title: 'Skills',
text: `Provide me a list of skills that Arthur Danjou masters.`
},
{
description: 'Generates a prompt to retrieve Arthur Danjou\'s detailed coding statistics and analytics powered by WakaTime, including programming languages, time spent coding, and productivity metrics.',
title: 'Stats',
text: `Provide me the stats of Arthur Danjou powered by Wakatime.`
},
{
description: 'Generates a prompt to retrieve the real-time status page of Arthur Danjou\'s homelab infrastructure, including uptime monitoring and incident reports powered by UptimeKuma.',
title: 'Status Page Activity',
text: `Provide me the status page activity of Arthur Danjou's homelab, including uptime and incidents.`
},
{
description: 'Generates a prompt to retrieve tools, software, and hardware used by Arthur Danjou, filtered by a specific category (homelab, IDE, hardware, or software).',
title: 'Uses by Category',
text: `Provide me the tools, software, and hardware used by Arthur Danjou in the category of {categoryName}.`
}
]
return prompts
},
{
maxAge: 60 * 60 * 24, // 24 hours
name: 'chat-prompts'
})

View File

@@ -1,14 +0,0 @@
export default defineMcpPrompt({
description: 'Generates a prompt to retrieve Arthur Danjou\'s current real-time activity status, including what he is currently working on.',
handler: async () => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Provide me the realtime activity of Arthur Danjou.`
}
}]
}
}
})

View File

@@ -1,14 +0,0 @@
export default defineMcpPrompt({
description: 'Generates a prompt to retrieve Arthur Danjou\'s contact information and social media links, including email, LinkedIn, GitHub, Twitter, Discord, and personal websites.',
handler: async () => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `How can I contact Arthur Danjou? Provide all contact methods and social links.`
}
}]
}
}
})

View File

@@ -1,14 +0,0 @@
export default defineMcpPrompt({
description: 'Generates a prompt to retrieve information about Arthur Danjou\'s personal hobbies, interests, and passions outside of professional work.',
handler: async () => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `What are the hobbies, interests and passions of Arthur Danjou?`
}
}]
}
}
})

View File

@@ -1,14 +0,0 @@
export default defineMcpPrompt({
description: 'Generates a prompt to retrieve the languages spoken by Arthur Danjou along with detailed proficiency levels for each language.',
handler: async () => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `What languages does Arthur Danjou speak and at what proficiency level?`
}
}]
}
}
})

View File

@@ -1,14 +0,0 @@
export default defineMcpPrompt({
description: 'Generates a prompt to retrieve comprehensive professional profile information about Arthur Danjou, including biography, location, availability status, career goals, and work preferences.',
handler: async () => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Provide me comprehensive profile information about Arthur Danjou including his bio, location, availability, career goals, and work preferences.`
}
}]
}
}
})

View File

@@ -1,14 +0,0 @@
export default defineMcpPrompt({
description: 'Generates a prompt to retrieve a comprehensive list of personal and professional projects developed by Arthur Danjou, showcasing his technical skills and achievements.',
handler: async () => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Provide me a list of projects done by Arthur Danjou.`
}
}]
}
}
})

View File

@@ -1,19 +0,0 @@
import { z } from 'zod'
export default defineMcpPrompt({
description: 'Generates a prompt to request and retrieve Arthur Danjou\'s professional resume in the specified language (English or French).',
inputSchema: {
lang: z.enum(['en', 'fr']).describe('The language for the resume: \'en\' for English or \'fr\' for French.')
},
handler: async ({ lang }) => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Provide me the link to download Arthur Danjou's resume in ${lang === 'en' ? 'English' : 'French'}.`
}
}]
}
}
})

View File

@@ -1,14 +0,0 @@
export default defineMcpPrompt({
description: 'Generates a prompt to retrieve a comprehensive list of technical skills, programming languages, frameworks, and tools mastered by Arthur Danjou.',
handler: async () => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Provide me a list of skills that Arthur Danjou masters.`
}
}]
}
}
})

View File

@@ -1,14 +0,0 @@
export default defineMcpPrompt({
description: 'Generates a prompt to retrieve Arthur Danjou\'s detailed coding statistics and analytics powered by WakaTime, including programming languages, time spent coding, and productivity metrics.',
handler: async () => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Provide me the stats of Arthur Danjou powered by Wakatime.`
}
}]
}
}
})

View File

@@ -1,14 +0,0 @@
export default defineMcpPrompt({
description: 'Generates a prompt to retrieve the real-time status page of Arthur Danjou\'s homelab infrastructure, including uptime monitoring and incident reports powered by UptimeKuma.',
handler: async () => {
return {
messages: [{
role: 'user',
content: {
type: 'text',
text: `Provide me the status page activity of Arthur Danjou's homelab, including uptime and incidents.`
}
}]
}
}
})

View File

@@ -1,20 +0,0 @@
import { z } from 'zod'
export default defineMcpPrompt({
description: 'Generates a prompt to retrieve tools, software, and hardware used by Arthur Danjou, filtered by a specific category (homelab, IDE, hardware, or software).',
inputSchema: {
categoryName: z.enum(['homelab', 'ide', 'hardware', 'software']).describe('The category to filter by: \'homelab\', \'ide\', \'hardware\', or \'software\'.')
},
handler: async ({ categoryName }) => {
return {
messages: [
{
role: 'user',
content: {
type: 'text',
text: `How can I view the setup of Arthur for this category : ${categoryName}?`
}
}
] }
}
})

View File

@@ -1,17 +0,0 @@
export default defineMcpResource({
title: 'Arthur Danjou - Contact Information & Social Media Links',
description: 'Contact information and social media links for Arthur Danjou, including email, LinkedIn, GitHub, Twitter, Discord, and personal websites',
uri: 'resource://artmcp/contact',
cache: '1 hour',
handler: async (uri: URL) => {
const result = await $fetch('/api/contact')
return {
contents: [{
uri: uri.toString(),
mimeType: 'text/json',
text: JSON.stringify(result, null, 2)
}]
}
}
})

View File

@@ -1,17 +0,0 @@
export default defineMcpResource({
title: 'Arthur Danjou - Education & Academic Background',
description: 'Arthur Danjou\'s educational background, including degrees, institutions, and academic achievements',
uri: 'resource://artmcp/education',
cache: '1 hour',
handler: async (uri: URL) => {
const result = await $fetch('/api/education')
return {
contents: [{
uri: uri.toString(),
mimeType: 'text/json',
text: JSON.stringify(result)
}]
}
}
})

View File

@@ -1,17 +0,0 @@
export default defineMcpResource({
title: 'Arthur Danjou - Experiences',
description: 'A detailed list of Arthur Danjou\'s professional work experiences, including roles, companies, and responsibilities',
uri: 'resource://artmcp/experiences',
cache: '1 hour',
handler: async (uri: URL) => {
const result = await $fetch('/api/experiences')
return {
contents: [{
uri: uri.toString(),
mimeType: 'text/json',
text: JSON.stringify(result)
}]
}
}
})

View File

@@ -1,17 +0,0 @@
export default defineMcpResource({
title: 'Arthur Danjou - Hobbies & Interests',
description: 'Arthur Danjou\'s personal hobbies, interests, and passions outside of professional work',
uri: 'resource://artmcp/hobbies',
cache: '1 hour',
handler: async (uri: URL) => {
const result = await $fetch('/api/hobbies')
return {
contents: [{
uri: uri.toString(),
mimeType: 'text/json',
text: JSON.stringify(result, null, 2)
}]
}
}
})

View File

@@ -1,17 +0,0 @@
export default defineMcpResource({
title: 'Arthur Danjou - Spoken Languages & Proficiency Levels',
description: 'Languages spoken by Arthur Danjou with detailed proficiency levels for each language',
uri: 'resource://artmcp/languages',
cache: '1 hour',
handler: async (uri: URL) => {
const result = await $fetch('/api/languages')
return {
contents: [{
uri: uri.toString(),
mimeType: 'text/json',
text: JSON.stringify(result, null, 2)
}]
}
}
})

View File

@@ -1,17 +0,0 @@
export default defineMcpResource({
title: 'Arthur Danjou - Professional Profile',
description: 'Comprehensive professional profile of Arthur Danjou, including biography, location, availability status, career goals, and work preferences',
uri: 'resource://artmcp/profile',
cache: '1 hour',
handler: async (uri: URL) => {
const result = await $fetch('/api/profile')
return {
contents: [{
uri: uri.toString(),
mimeType: 'text/json',
text: JSON.stringify(result)
}]
}
}
})

View File

@@ -1,17 +0,0 @@
export default defineMcpResource({
title: 'Arthur Danjou - Projects Portfolio',
description: 'A comprehensive collection of projects developed by Arthur Danjou, showcasing technical skills and achievements',
uri: 'resource://artmcp/projects',
cache: '1 hour',
handler: async (uri: URL) => {
const result = await $fetch('/api/projects')
return {
contents: [{
uri: uri.toString(),
mimeType: 'text/json',
text: JSON.stringify(result)
}]
}
}
})

View File

@@ -1,17 +0,0 @@
export default defineMcpResource({
title: 'Arthur Danjou - Skills',
description: 'A comprehensive list of technical skills, programming languages, frameworks, and tools mastered by Arthur Danjou',
cache: '1 hour',
uri: 'resource://artmcp/skills',
handler: async (uri: URL) => {
const result = await $fetch('/api/skills')
return {
contents: [{
uri: uri.toString(),
mimeType: 'text/json',
text: JSON.stringify(result, null, 2)
}]
}
}
})

View File

@@ -1,17 +0,0 @@
export default defineMcpResource({
title: 'Arthur Danjou - Tech Stack & Tools',
description: 'A curated list of tools, software, and hardware used by Arthur Danjou, organized by categories (homelab, IDE, hardware, software)',
uri: 'resource://artmcp/uses',
cache: '1 hour',
handler: async (uri: URL) => {
const result = await $fetch('/api/uses')
return {
contents: [{
uri: uri.toString(),
mimeType: 'text/json',
text: JSON.stringify(result, null, 2)
}]
}
}
})

View File

@@ -0,0 +1,39 @@
import { z } from 'zod'
export default defineMcpTool({
description: 'Read a resource from the server API',
inputSchema: {
resource: z.enum([
'contact',
'education',
'experiences',
'hobbies',
'languages',
'profile',
'projects',
'skills',
'uses'
]).describe('resource name')
},
handler: async ({ resource }) => {
try {
const data = await $fetch(`/api/${resource}`)
return {
content: [{
type: 'text',
text: JSON.stringify(data, null, 2)
}]
}
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
}
}
}
})

View File

@@ -1,14 +1,12 @@
/* eslint-disable */
// Generated by Wrangler by running `wrangler types` (hash: 0e830e66d194b5321c2ceb2f6b8949ed)
// Runtime types generated with workerd@1.20251213.0 2025-12-13 nodejs_compat
// Generated by Wrangler by running `wrangler types` (hash: 7b4844cbbc6894946bc3782cc10972c6)
// Runtime types generated with workerd@1.20251217.0 2025-12-13 nodejs_compat
declare namespace Cloudflare {
interface GlobalProps {
mainModule: typeof import("./.output/server/index");
}
interface Env {
CACHE: KVNamespace;
NUXT_DISCORD_ID: string;
NUXT_DISCORD_TOKEN: string;
NUXT_DISCORD_USER_ID: string;
NUXT_WAKATIME_CODING: string;
NUXT_WAKATIME_EDITORS: string;
@@ -27,7 +25,7 @@ type StringifyValues<EnvType extends Record<string, unknown>> = {
[Binding in keyof EnvType]: EnvType[Binding] extends string ? EnvType[Binding] : string;
};
declare namespace NodeJS {
interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "NUXT_DISCORD_ID" | "NUXT_DISCORD_TOKEN" | "NUXT_DISCORD_USER_ID" | "NUXT_WAKATIME_CODING" | "NUXT_WAKATIME_EDITORS" | "NUXT_WAKATIME_LANGUAGES" | "NUXT_WAKATIME_OS" | "NUXT_WAKATIME_USER_ID" | "NUXT_STATUS_PAGE" | "STUDIO_GITHUB_CLIENT_ID" | "STUDIO_GITHUB_CLIENT_SECRET">> {}
interface ProcessEnv extends StringifyValues<Pick<Cloudflare.Env, "NUXT_DISCORD_USER_ID" | "NUXT_WAKATIME_CODING" | "NUXT_WAKATIME_EDITORS" | "NUXT_WAKATIME_LANGUAGES" | "NUXT_WAKATIME_OS" | "NUXT_WAKATIME_USER_ID" | "NUXT_STATUS_PAGE" | "STUDIO_GITHUB_CLIENT_ID" | "STUDIO_GITHUB_CLIENT_SECRET">> {}
}
// Begin runtime types

51
wrangler.jsonc Normal file
View File

@@ -0,0 +1,51 @@
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "artapi",
"compatibility_date": "2025-12-13",
"compatibility_flags": [
"nodejs_compat",
],
"preview_urls": true,
"workers_dev": true,
"main": "./.output/server/index.mjs",
"routes": [
{
"pattern": "api.arthurdanjou.fr",
"zone_name": "arthurdanjou.fr",
"custom_domain": true
}
],
"placement": {
"mode": "smart",
},
"assets": {
"binding": "ASSETS",
"directory": "./.output/public/"
},
"d1_databases": [
{
"binding": "DB",
"database_id": "d6c86b05-f4aa-4dcf-8962-e0b0d538d302"
}
],
"kv_namespaces": [
{
"binding": "CACHE",
"id": "0158163e35cf4804832a38863aed8ee9",
}
],
"observability": {
"enabled": true,
"logs": {
"enabled": true,
"head_sampling_rate": 1,
"persist": true,
"invocation_logs": true
},
"traces": {
"enabled": true,
"head_sampling_rate": 1,
"persist": true
}
}
}