Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Apec

БесплатноНе проверен

MCP server that exposes the APEC candidate-profile API as tools for any MCP-compatible AI assistant, enabling profile management, job-search updates, and data r

GitHubEmbed

Описание

MCP server that exposes the APEC candidate-profile API as tools for any MCP-compatible AI assistant, enabling profile management, job-search updates, and data retrieval.

README

MCP (Model Context Protocol) server that exposes the APEC candidate-profile API as tools for any MCP-compatible AI assistant.


API Analysis

API Type

Proprietary REST / JSON over HTTPS.
Not publicly documented. Discovered via browser network capture (HAR files).
Base URL: https://www.apec.fr/cms/webservices/

Authentication

Email/password auto-login — no manual cookie handling required.

The server posts credentials to /.apec-login.do at first use and caches the resulting session cookies in ~/.apec_session.json. On 401/403 the session is automatically invalidated and a fresh login is attempted before retrying.

Env var Purpose
APEC_EMAIL APEC account email
APEC_PASSWORD APEC account password
APEC_ID_COMPTE_CADRE Numeric account ID (from your APEC profile URL)
APEC_ID_PROFIL_CADRE Numeric profile ID (from your APEC profile URL)
APEC_SESSION_FILE Override session cache path (default: ~/.apec_session.json)
APEC_COOKIES Manual cookie override — takes precedence over auto-login (fallback if DataDome blocks login)

Reflection / Introspection

There is no public API schema (no OpenAPI spec, no GraphQL introspection).
The API was reverse-engineered from three browser sessions:

  • www.apec.fr.har — initial profile creation
  • www2.apec.fr.har — portfolio/strengths editing
  • www3.apec.fr.har — full profile editing (business card, photo upload, reference data)

The backend appears to be a Java Spring application (JSESSIONID, Jahia CMS static assets).


API Endpoints

Profile

Method Path Description
GET /profilCadre/{idCompte}/complete Full profile (the canonical object)
POST /profilCadre/updateExperiencesEtFormations Save experiences + education
POST /profilCadre/updateInformationsComplementaires Save job-search preferences
POST /profilCadre/updateAtouts Save skills + strengths
POST /profilCadre/carteDeVisite Save personal / contact info
POST /profilCadre/validation/{SECTION} Validate before saving
GET /profilCadre/shouldDisplayEncart UI display flag
GET /profilCadre/photoUploadFolders Photo upload paths

Stats

Method Path Description
GET /profilVu/{idProfil}/nombreVuesDepuisPublication Total profile views
GET /profilVu/{idProfil}/dureePublication Days since publication
GET /profilVu/{idProfil}/tendanceVues Views trend (float)
POST /echangeProfil/count Unread recruiter messages
POST /panierProfil/countInterlocuteursRetenir Saved contacts count

Search / Autocomplete

Method Path Description
GET /autocompletion/distinctMetierAutocomplete Job-title search
GET /autocompletion/autocomplete General search

Reference Data

Method Path Description
POST /referentielstatique/presentations/visuels/liste/hierarchie Enum lists (869 entries)
GET /referentielstatique/fonctions/metiers/{id}/organisation Métier taxonomy node
GET /referentielstatique/presentations/code/LISTE_MONDE/visuels Country list

Identification & CV

Method Path Description
GET /identification/apecuser Current user info
GET /cv/cvFichiersByIdCompte Uploaded CV files
GET /pushRecoOffre/abonnement/cadre/{id} Job-recommendation subscription

Job Offers

Method Path Description
POST /affiniteOrchestration/matching-candidat AI-matched offers for the candidate
GET /offre/public Full offer details by numeroOffre
POST /rechercheAffinite/rebond Similar/related offers
GET /pageEntreprise/public/{id} Public company profile

Applications & Saved Jobs

Method Path Description
POST /candidature/siteRh Submit an APEC-hosted application
POST /candidature/liste List past applications
POST /candidature/liste/count Check if already applied to an offer
POST /panierOffre/liste List saved/bookmarked offers

Data Model (TypeSpec)

import "@typespec/rest";
import "@typespec/http";

using TypeSpec.Rest;
using TypeSpec.Http;

// ── Shared ──────────────────────────────────────────────────────────────

model Audit {
  dateCreation: int64;        // epoch ms
  dateModification: int64 | null;
  utilisateurCreation: string;
  utilisateurModification: string | null;
}

// ── Reference / enum entries ─────────────────────────────────────────────

model ReferenceItem {
  codePresentation: string;
  idNomenclature: int64;
  codeNomenclature: string;
  idOrganisation: int64;
  idOrganisationParent: int64 | null;
  libelle: string;
  niveau: int32;
  ordre: int32;
}

// ── Sub-models ───────────────────────────────────────────────────────────

model Experience {
  intitulePoste: string;
  idNomFonction: int64;
  idNomMetier: int64;
  dateEntree: string;           // ISO 8601
  dateSortie: string | null;
  nomEntreprise: string;
  expNonCadre: boolean;
  numeroOrdre: int32;
  audit: Audit;
}

model Formation {
  intituleFormation: string;
  dateEntree: string;           // ISO 8601
  dateSortie: string | null;
  idNomDiscipline: string;
  idNomNiveau: string;
  organismeFormation: string | null;
  numeroOrdre: int32;
  audit: Audit;
}

enum CompetenceType {
  LANGUE,
  SAVOIR_ETRE,
  TECHNIQUE,
  METIER,
}

model Competence {
  libelle: string;
  type: CompetenceType;
  idNomCompetence: int64;
  idNomNiveau: int64 | null;
  miseEnAvant: boolean;
  idProfilCadre: int64;
  audit: Audit;
  id: int64;
}

model AdressePostale {
  adresseNumeroEtVoie: string;
  adresseCodePostal: string;
  adresseVille: string;
  adresseBatimentImmResidence: string | null;
  adresseComplementAdresse: string | null;
  idPays: int64;
  audit: Audit;
  id: int64;
}

model SouhaitSecteur   { idNomSecteurActivite: int64; audit: Audit; id: int64; }
model SouhaitFonction  { idNomFonction: int64; idNomMetier: int64; audit: Audit; id: int64; }
model SouhaitContrat   { idNomTypeContrat: int64; audit: Audit; id: int64; }
model SouhaitEntreprise{ idNomTailleEntreprise: int64; audit: Audit; id: int64; }
model SouhaitTemps     { idNomTempsTravail: int64; audit: Audit; id: int64; }
model SouhaitMode      { idNomModeTravail: int64; audit: Audit; id: int64; }
model SouhaitLieu      { idNomLieu: int64; distance: int32 | null; audit: Audit; id: int64; }

// ── Full profile ─────────────────────────────────────────────────────────

model ProfilCadre {
  id: int64;
  idProfilCadre: int64;
  idCompteCadre: int64;
  idNomStatut: int64;
  idNomStatutCandidat: int64;

  // Personal info
  idNomCivilite: int64;
  nom: string;
  prenom: string;
  dateNaissance: int64;           // epoch ms
  adressePostale: AdressePostale;
  adresseEmail: string;
  numeroTelephoneMobile: string;
  lienLinkedin: string | null;
  photo: string | null;

  // Professional summary
  metierSouhaite: string;
  objectifProfessionnel: string | null;
  pointsClesProfessionnels: string | null;
  idNomAnneesExperience: int64;
  idNomDelaiDisponibilite: int64;
  remunerationMinimale: float32;
  indicateurMasquerSalaire: boolean;
  pretPourRecrutement: boolean;

  // Preferences
  souhaitsSecteurs: SouhaitSecteur[];
  souhaitsFonctions: SouhaitFonction[];
  souhaitsContrats: SouhaitContrat[];
  souhaitsEnts: SouhaitEntreprise[];
  souhaitsTemps: SouhaitTemps[];
  souhaitsModes: SouhaitMode[];
  souhaitsLieux: SouhaitLieu[];

  // Experiences & formations
  experiencesCles: Experience[];
  formationsCles: Formation[];

  // Skills
  competences: Competence[];
  atouts: string[];
  portfolios: unknown[];

  // CV
  idCvFichier: int64 | null;

  // Completion indicators
  tauxRemplissage: int32;
  indicateurCompletCompetence: boolean;
  indicateurCompletCompMea: boolean;
  indicateurCompletPortfolio: boolean;

  // Versioning
  numeroVersion: int32;
  numeroVersionCadre: int32;
  audit: Audit;
  auditCadre: Audit;
}

// ── User identity ────────────────────────────────────────────────────────

model ApecUser {
  id: string;
  numeroCompte: string;
  nom: string;
  prenom: string;
  email: string;
  cadre: boolean;
  actif: boolean;
  token: string;
  sessionId: string;
}

MCP Tools

Tool APEC endpoint Description
get_user_info GET /identification/apecuser Authenticated user details
get_profile GET /profilCadre/{id}/complete Full profile (use as base for updates)
get_profile_stats GET /profilVu/… × 3 Views, duration, trend
get_cv_files GET /cv/cvFichiersByIdCompte Uploaded CVs
get_unread_messages_count POST /echangeProfil/count Unread recruiter messages
validate_section POST /profilCadre/validation/{SECTION} Validate before saving
update_experiences_formations POST /profilCadre/updateExperiencesEtFormations Experiences + education
update_informations_complementaires POST /profilCadre/updateInformationsComplementaires Job-search preferences
update_atouts POST /profilCadre/updateAtouts Skills + strengths
update_carte_de_visite POST /profilCadre/carteDeVisite Personal / contact info
search_metiers GET /autocompletion/distinctMetierAutocomplete Job-title autocomplete
autocomplete GET /autocompletion/autocomplete General search
get_metier_organisation GET /referentielstatique/fonctions/… Métier taxonomy
get_reference_lists POST /referentielstatique/…/hierarchie Enum reference data
get_matching_jobs POST /affiniteOrchestration/matching-candidat AI-matched job offers (paginated)
get_job_offer GET /offre/public Full offer details
get_similar_jobs POST /rechercheAffinite/rebond Related offers
get_company_info GET /pageEntreprise/public/{id} Public company profile
apply_to_job POST /candidature/siteRh Submit APEC-hosted application
list_applications POST /candidature/liste Past applications (paginated)
check_applied_to_offer POST /candidature/liste/count Check if already applied
list_saved_jobs POST /panierOffre/liste Saved/bookmarked offers
refresh_session POST /.apec-login.do Force re-login and return user info

Update pattern

All update endpoints require the complete profile object (not a partial patch).

profile = get_profile()
profile["experiencesCles"].append({...})
update_experiences_formations(profile)

Key Use Case — Fill Profile from LinkedIn or CV

The intended end-to-end workflow:

1. [LinkedIn MCP] get_my_profile()           → raw LinkedIn data
   OR parse CV text (PDF / plain text)

2. [APEC MCP]     get_reference_lists([…])  → resolve enum IDs
                  search_metiers(q=…)        → find idNomMetier / idNomFonction

3. AI assistant maps fields:
     LinkedIn title      → metierSouhaite + experiencesCles[].intitulePoste
     LinkedIn positions  → experiencesCles[]
     LinkedIn education  → formationsCles[]
     LinkedIn skills     → competences[]
     LinkedIn location   → adressePostale + souhaitsLieux[]

4. [APEC MCP]     get_profile()                     → fetch current profile
                  update_experiences_formations(…)  → save
                  update_informations_complementaires(…)
                  update_atouts(…)
                  update_carte_de_visite(…)

Setup & Run

Prerequisites

  • uv installed

Install

git clone <repo>
cd apec-mcp
uv sync
cp .env.example .env
# Fill in APEC_EMAIL, APEC_PASSWORD, APEC_ID_COMPTE_CADRE, APEC_ID_PROFIL_CADRE

Local development (SSE)

uv run apec-mcp               # SSE on http://localhost:8080/sse

MCP client config (stdio — recommended)

Pass credentials directly in the env block. Use the absolute path to the venv binary so the config works regardless of which directory the MCP client opens.

{
  "mcpServers": {
    "apec": {
      "command": "/path/to/apec-mcp/.venv/bin/apec-mcp",
      "args": [],
      "env": {
        "MCP_TRANSPORT": "stdio",
        "APEC_EMAIL": "[email protected]",
        "APEC_PASSWORD": "yourpassword",
        "APEC_ID_COMPTE_CADRE": "123456789",
        "APEC_ID_PROFIL_CADRE": "1234567"
      }
    }
  }
}

For Claude Code place this block in ~/.claude.json under mcpServers so all sessions share it.

Docker (SSE mode, optional)

docker build -t apec-mcp:latest .
docker run -d --name apec-mcp-server -p 8081:8080 --env-file .env \
  -e MCP_TRANSPORT=sse apec-mcp:latest

Then point your MCP client at http://localhost:8081/sse.

docker compose may fail to resolve build-cache image digests in some container-in-container environments (OrbStack socktainer, Docker-in-Docker). Plain docker run is more reliable in those setups.


Session Refresh

Session cookies are cached automatically in ~/.apec_session.json and refreshed on 401/403. No manual action is normally needed.

If auto-login is blocked by DataDome:

  1. Open https://www.apec.fr → log in manually
  2. DevTools → Network → any /cms/webservices/* request → Request Headers → Cookie
  3. Add APEC_COOKIES=<value> to .env — this overrides auto-login

from github.com/clement-igonet/apec-mcp

Установка Apec

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/clement-igonet/apec-mcp

FAQ

Apec MCP бесплатный?

Да, Apec MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Apec?

Нет, Apec работает без API-ключей и переменных окружения.

Apec — hosted или self-hosted?

Доступен hosted-вариант: Unyly запускает сервер в облаке, локальная установка не обязательна.

Как установить Apec в Claude Desktop, Claude Code или Cursor?

Открой Apec на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Apec with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории ai