boondmanager-mcp-server 1.7.2 → 1.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +129 -28
- package/dist/resources/index.js.map +1 -1
- package/dist/services/dictionary.d.ts +52 -0
- package/dist/services/dictionary.d.ts.map +1 -0
- package/dist/services/dictionary.js +73 -0
- package/dist/services/dictionary.js.map +1 -0
- package/dist/tools/application.d.ts.map +1 -1
- package/dist/tools/application.js +43 -18
- package/dist/tools/application.js.map +1 -1
- package/manifest.json +3 -2
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAwKzE,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB;;;;GAOhC,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA2D5D"}
|
package/dist/resources/index.js
CHANGED
|
@@ -1,32 +1,129 @@
|
|
|
1
1
|
import { apiRequest } from "../services/boond-client.js";
|
|
2
|
+
import { getDictionary, resolveDictionaryPath } from "../services/dictionary.js";
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Curated dictionaries surfaced as static MCP resources. Slugs follow the
|
|
5
|
+
* historical "kind/entity" naming for backward compatibility with client URIs;
|
|
6
|
+
* the API path mapping reflects the actual BoondManager `/application/dictionary`
|
|
7
|
+
* response structure (cf. `data.setting.*`, `data.country`, `data.languages`).
|
|
8
|
+
*
|
|
9
|
+
* Slugs that do not map to a real path in the API (e.g. `states/absences`,
|
|
10
|
+
* `typeOf/candidates`) are intentionally absent.
|
|
6
11
|
*/
|
|
7
12
|
const DICTIONARIES = [
|
|
8
13
|
// states/* — used to translate the integer `state` attribute on entities
|
|
9
|
-
{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
{
|
|
15
|
+
slug: "states/resources",
|
|
16
|
+
path: "setting.state.resource",
|
|
17
|
+
title: "États ressources",
|
|
18
|
+
description: "Libellés des états de ressource (collaborateur).",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
slug: "states/candidates",
|
|
22
|
+
path: "setting.state.candidate",
|
|
23
|
+
title: "États candidats",
|
|
24
|
+
description: "Libellés des états de candidat.",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
slug: "states/contacts",
|
|
28
|
+
path: "setting.state.contact",
|
|
29
|
+
title: "États contacts",
|
|
30
|
+
description: "Libellés des états de contact.",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
slug: "states/companies",
|
|
34
|
+
path: "setting.state.company",
|
|
35
|
+
title: "États sociétés",
|
|
36
|
+
description: "Libellés des états de société.",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
slug: "states/opportunities",
|
|
40
|
+
path: "setting.state.opportunity",
|
|
41
|
+
title: "États opportunités",
|
|
42
|
+
description: "Libellés des états d'opportunité commerciale.",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
slug: "states/projects",
|
|
46
|
+
path: "setting.state.project",
|
|
47
|
+
title: "États projets",
|
|
48
|
+
description: "Libellés des états de projet/mission.",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
slug: "states/invoices",
|
|
52
|
+
path: "setting.state.invoice",
|
|
53
|
+
title: "États factures",
|
|
54
|
+
description: "Libellés des états de facture client.",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
slug: "states/orders",
|
|
58
|
+
path: "setting.state.order",
|
|
59
|
+
title: "États bons de commande",
|
|
60
|
+
description: "Libellés des états de bon de commande.",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
slug: "states/positionings",
|
|
64
|
+
path: "setting.state.positioning",
|
|
65
|
+
title: "États positionnements",
|
|
66
|
+
description: "Libellés des états de positionnement.",
|
|
67
|
+
},
|
|
19
68
|
// typeOf/* — used to translate the integer `typeOf` attribute on entities
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
69
|
+
{
|
|
70
|
+
slug: "typeOf/resources",
|
|
71
|
+
path: "setting.typeOf.resource",
|
|
72
|
+
title: "Types ressources",
|
|
73
|
+
description: "Types de ressource (interne, sous-traitant, freelance...).",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
slug: "typeOf/contacts",
|
|
77
|
+
path: "setting.typeOf.contact",
|
|
78
|
+
title: "Types contacts",
|
|
79
|
+
description: "Types de contact.",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
slug: "typeOf/projects",
|
|
83
|
+
path: "setting.typeOf.project",
|
|
84
|
+
title: "Types projets",
|
|
85
|
+
description: "Types de projet (régie, forfait, produit...).",
|
|
86
|
+
},
|
|
87
|
+
// Skills / referential
|
|
88
|
+
{
|
|
89
|
+
slug: "tools",
|
|
90
|
+
path: "setting.tool",
|
|
91
|
+
title: "Outils / Technos",
|
|
92
|
+
description: "Catalogue des outils et technologies utilisables sur les ressources et candidats (Java, AWS, ...).",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
slug: "expertiseAreas",
|
|
96
|
+
path: "setting.expertiseArea",
|
|
97
|
+
title: "Domaines d'expertise",
|
|
98
|
+
description: "Domaines d'expertise métier (DevOps, Data, Frontend, ...).",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
slug: "experiences",
|
|
102
|
+
path: "setting.experience",
|
|
103
|
+
title: "Niveaux d'expérience",
|
|
104
|
+
description: "Niveaux d'expérience (junior, confirmé, senior, ...).",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
slug: "activityAreas",
|
|
108
|
+
path: "setting.activityArea",
|
|
109
|
+
title: "Secteurs d'activité",
|
|
110
|
+
description: "Secteurs d'activité des sociétés clientes.",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
slug: "mobilityAreas",
|
|
114
|
+
path: "setting.mobilityArea",
|
|
115
|
+
title: "Mobilités",
|
|
116
|
+
description: "Zones de mobilité géographique.",
|
|
117
|
+
},
|
|
26
118
|
// Global lookups
|
|
27
|
-
{ slug: "countries", title: "Pays", description: "Liste des pays (codes ISO + libellés)." },
|
|
28
|
-
{ slug: "currencies", title: "Devises", description: "Liste des devises supportées." },
|
|
29
|
-
{
|
|
119
|
+
{ slug: "countries", path: "country", title: "Pays", description: "Liste des pays (codes ISO + libellés)." },
|
|
120
|
+
{ slug: "currencies", path: "setting.currency", title: "Devises", description: "Liste des devises supportées." },
|
|
121
|
+
{
|
|
122
|
+
slug: "languages",
|
|
123
|
+
path: "languages",
|
|
124
|
+
title: "Langues",
|
|
125
|
+
description: "Langues d'interface BoondManager (fr, en, es).",
|
|
126
|
+
},
|
|
30
127
|
];
|
|
31
128
|
/** URI prefix under which all dictionaries are exposed. */
|
|
32
129
|
const DICTIONARY_URI_PREFIX = "boond://dictionary/";
|
|
@@ -52,15 +149,19 @@ export function registerAllResources(server) {
|
|
|
52
149
|
description: dict.description,
|
|
53
150
|
mimeType: "application/json",
|
|
54
151
|
}, async () => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
152
|
+
const { payload } = await getDictionary();
|
|
153
|
+
const node = resolveDictionaryPath(payload, dict.path);
|
|
154
|
+
const body = node === undefined
|
|
155
|
+
? {
|
|
156
|
+
error: `Path "${dict.path}" not found in BoondManager dictionary. The upstream API may have changed — please open an issue.`,
|
|
157
|
+
}
|
|
158
|
+
: node;
|
|
58
159
|
return {
|
|
59
160
|
contents: [
|
|
60
161
|
{
|
|
61
162
|
uri,
|
|
62
163
|
mimeType: "application/json",
|
|
63
|
-
text: JSON.stringify(
|
|
164
|
+
text: JSON.stringify(body, null, 2),
|
|
64
165
|
},
|
|
65
166
|
],
|
|
66
167
|
};
|
|
@@ -71,8 +172,8 @@ export function registerAllResources(server) {
|
|
|
71
172
|
// full /application/current-user payload.
|
|
72
173
|
server.registerResource("application/current-user", CURRENT_USER_URI, {
|
|
73
174
|
title: "Utilisateur courant",
|
|
74
|
-
description: "Profil de l'utilisateur authentifié auprès de l'API BoondManager (id, agence, permissions). "
|
|
75
|
-
|
|
175
|
+
description: "Profil de l'utilisateur authentifié auprès de l'API BoondManager (id, agence, permissions). " +
|
|
176
|
+
"Utile pour résoudre 'mon ID' avant un appel filtré par perimeterManagers.",
|
|
76
177
|
mimeType: "application/json",
|
|
77
178
|
}, async () => {
|
|
78
179
|
const response = await apiRequest("/application/current-user");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { JsonApiResponse } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Cache du dictionnaire BoondManager.
|
|
4
|
+
*
|
|
5
|
+
* L'API BoondManager expose **un seul endpoint** `GET /application/dictionary`
|
|
6
|
+
* qui retourne l'intégralité des dictionnaires (états, types, pays, devises,
|
|
7
|
+
* langues, outils, expertises, …) en une seule réponse JSON. La structure
|
|
8
|
+
* pertinente pour les outils du serveur est `data.setting.*` et
|
|
9
|
+
* `data.{country,languages}` (cf. RAML `schemas/application/dictionary.json`).
|
|
10
|
+
*
|
|
11
|
+
* Sans cache, chaque lecture de ressource ou appel à `boond_application_dictionary`
|
|
12
|
+
* forcerait un appel HTTP de plusieurs centaines de Ko, alors que le contenu
|
|
13
|
+
* change rarement (libellés d'états, types métier, …). On cache donc en mémoire
|
|
14
|
+
* pour la durée du process, avec un TTL configurable.
|
|
15
|
+
*
|
|
16
|
+
* Concurrent fetches sont dédupliqués via une promesse partagée pour éviter
|
|
17
|
+
* de marteler l'API quand plusieurs ressources sont lues en parallèle au
|
|
18
|
+
* démarrage d'une session MCP.
|
|
19
|
+
*/
|
|
20
|
+
export type DictionaryLanguage = "fr" | "en" | "es";
|
|
21
|
+
interface CacheEntry {
|
|
22
|
+
payload: JsonApiResponse;
|
|
23
|
+
fetchedAt: number;
|
|
24
|
+
language: DictionaryLanguage;
|
|
25
|
+
}
|
|
26
|
+
export interface GetDictionaryOptions {
|
|
27
|
+
language?: DictionaryLanguage;
|
|
28
|
+
/** Bypass the cache and re-fetch. */
|
|
29
|
+
force?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Returns the full BoondManager dictionary payload, fetching once per TTL.
|
|
33
|
+
* Concurrent calls share the same in-flight request.
|
|
34
|
+
*/
|
|
35
|
+
export declare function getDictionary(opts?: GetDictionaryOptions): Promise<CacheEntry>;
|
|
36
|
+
/**
|
|
37
|
+
* Resolves a dotted path inside the dictionary `data` object.
|
|
38
|
+
*
|
|
39
|
+
* Examples:
|
|
40
|
+
* "setting.state.resource" → array of resource states
|
|
41
|
+
* "setting.tool" → array of tools / technos
|
|
42
|
+
* "country" → array of countries
|
|
43
|
+
* "languages" → array of UI languages
|
|
44
|
+
* "setting.state.resource[0]" → not supported (no bracket notation; pass plain dotted path)
|
|
45
|
+
*
|
|
46
|
+
* Returns `undefined` if any segment is missing.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveDictionaryPath(payload: JsonApiResponse, path: string): unknown;
|
|
49
|
+
/** Reset the cache. Exposed for tests. */
|
|
50
|
+
export declare function resetDictionaryCacheForTests(): void;
|
|
51
|
+
export {};
|
|
52
|
+
//# sourceMappingURL=dictionary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictionary.d.ts","sourceRoot":"","sources":["../../src/services/dictionary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEpD,UAAU,UAAU;IAClB,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,kBAAkB,CAAC;CAC9B;AAcD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,qCAAqC;IACrC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,GAAE,oBAAyB,GAAG,OAAO,CAAC,UAAU,CAAC,CAuBxF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAcrF;AAED,0CAA0C;AAC1C,wBAAgB,4BAA4B,IAAI,IAAI,CAGnD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { apiRequest } from "./boond-client.js";
|
|
2
|
+
const DEFAULT_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
3
|
+
function resolveTtlMs() {
|
|
4
|
+
const raw = process.env["BOOND_DICTIONARY_TTL_MS"];
|
|
5
|
+
if (!raw)
|
|
6
|
+
return DEFAULT_TTL_MS;
|
|
7
|
+
const n = Number(raw);
|
|
8
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_TTL_MS;
|
|
9
|
+
}
|
|
10
|
+
let cache = null;
|
|
11
|
+
let inFlight = null;
|
|
12
|
+
/**
|
|
13
|
+
* Returns the full BoondManager dictionary payload, fetching once per TTL.
|
|
14
|
+
* Concurrent calls share the same in-flight request.
|
|
15
|
+
*/
|
|
16
|
+
export async function getDictionary(opts = {}) {
|
|
17
|
+
const language = opts.language ?? "fr";
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
if (!opts.force && cache !== null && cache.language === language && now - cache.fetchedAt < resolveTtlMs()) {
|
|
20
|
+
return cache;
|
|
21
|
+
}
|
|
22
|
+
if (inFlight)
|
|
23
|
+
return inFlight;
|
|
24
|
+
inFlight = (async () => {
|
|
25
|
+
try {
|
|
26
|
+
const payload = await apiRequest("/application/dictionary", "GET", undefined, {
|
|
27
|
+
language,
|
|
28
|
+
});
|
|
29
|
+
cache = { payload, fetchedAt: Date.now(), language };
|
|
30
|
+
return cache;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
inFlight = null;
|
|
34
|
+
}
|
|
35
|
+
})();
|
|
36
|
+
return inFlight;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolves a dotted path inside the dictionary `data` object.
|
|
40
|
+
*
|
|
41
|
+
* Examples:
|
|
42
|
+
* "setting.state.resource" → array of resource states
|
|
43
|
+
* "setting.tool" → array of tools / technos
|
|
44
|
+
* "country" → array of countries
|
|
45
|
+
* "languages" → array of UI languages
|
|
46
|
+
* "setting.state.resource[0]" → not supported (no bracket notation; pass plain dotted path)
|
|
47
|
+
*
|
|
48
|
+
* Returns `undefined` if any segment is missing.
|
|
49
|
+
*/
|
|
50
|
+
export function resolveDictionaryPath(payload, path) {
|
|
51
|
+
const trimmed = path.trim();
|
|
52
|
+
if (!trimmed)
|
|
53
|
+
return undefined;
|
|
54
|
+
const parts = trimmed.split(".");
|
|
55
|
+
// The dictionary payload is `{ meta, data: { setting, country, languages, ... } }`.
|
|
56
|
+
// We always look under `data` so callers don't have to repeat it.
|
|
57
|
+
const root = payload.data;
|
|
58
|
+
let node = root ?? payload;
|
|
59
|
+
for (const part of parts) {
|
|
60
|
+
if (node === null || typeof node !== "object")
|
|
61
|
+
return undefined;
|
|
62
|
+
node = node[part];
|
|
63
|
+
if (node === undefined)
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
return node;
|
|
67
|
+
}
|
|
68
|
+
/** Reset the cache. Exposed for tests. */
|
|
69
|
+
export function resetDictionaryCacheForTests() {
|
|
70
|
+
cache = null;
|
|
71
|
+
inFlight = null;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=dictionary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictionary.js","sourceRoot":"","sources":["../../src/services/dictionary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AA8B/C,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,SAAS;AAEhD,SAAS,YAAY;IACnB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IACnD,IAAI,CAAC,GAAG;QAAE,OAAO,cAAc,CAAC;IAChC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;AAC1D,CAAC;AAED,IAAI,KAAK,GAAsB,IAAI,CAAC;AACpC,IAAI,QAAQ,GAA+B,IAAI,CAAC;AAQhD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAA6B,EAAE;IACjE,MAAM,QAAQ,GAAuB,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,GAAG,YAAY,EAAE,EAAE,CAAC;QAC3G,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,QAAQ,GAAG,CAAC,KAAK,IAAI,EAAE;QACrB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,yBAAyB,EAAE,KAAK,EAAE,SAAS,EAAE;gBAC5E,QAAQ;aACT,CAAC,CAAC;YACH,KAAK,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC;YACrD,OAAO,KAAK,CAAC;QACf,CAAC;gBAAS,CAAC;YACT,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAwB,EAAE,IAAY;IAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,oFAAoF;IACpF,kEAAkE;IAClE,MAAM,IAAI,GAAI,OAAyC,CAAC,IAAI,CAAC;IAC7D,IAAI,IAAI,GAAY,IAAI,IAAI,OAAO,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAChE,IAAI,GAAI,IAAgC,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;IAC3C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,4BAA4B;IAC1C,KAAK,GAAG,IAAI,CAAC;IACb,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../../src/tools/application.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../../src/tools/application.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAezE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAiFhE"}
|
|
@@ -1,27 +1,41 @@
|
|
|
1
1
|
import { DictionaryGetSchema } from "../schemas/index.js";
|
|
2
|
+
import { CHARACTER_LIMIT } from "../constants.js";
|
|
2
3
|
import { apiRequest, formatDetailResponse } from "../services/boond-client.js";
|
|
4
|
+
import { getDictionary, resolveDictionaryPath } from "../services/dictionary.js";
|
|
5
|
+
function formatDictionaryNode(node) {
|
|
6
|
+
const text = JSON.stringify(node, null, 2);
|
|
7
|
+
if (text.length > CHARACTER_LIMIT) {
|
|
8
|
+
return text.substring(0, CHARACTER_LIMIT) + "\n\n[Résultat tronqué...]";
|
|
9
|
+
}
|
|
10
|
+
return text;
|
|
11
|
+
}
|
|
3
12
|
export function registerApplicationTools(server) {
|
|
4
13
|
// Get dictionary
|
|
5
14
|
server.registerTool("boond_application_dictionary", {
|
|
6
15
|
title: "Récupérer un dictionnaire BoondManager",
|
|
7
|
-
description: `Récupère un dictionnaire de référence
|
|
16
|
+
description: `Récupère un dictionnaire de référence BoondManager (états, types, pays, devises, langues, outils, expertises, ...).
|
|
17
|
+
|
|
18
|
+
L'API expose un seul endpoint \`/application/dictionary\` qui renvoie tout — le serveur le cache (TTL 1h, configurable via \`BOOND_DICTIONARY_TTL_MS\`) et extrait un sous-arbre par chemin dotté.
|
|
8
19
|
|
|
9
20
|
Args:
|
|
10
|
-
- dictionaryType (string):
|
|
11
|
-
- "
|
|
12
|
-
- "typeOf
|
|
13
|
-
- "
|
|
14
|
-
- "
|
|
15
|
-
- "
|
|
16
|
-
- "
|
|
17
|
-
- "
|
|
18
|
-
- "
|
|
19
|
-
- "
|
|
20
|
-
- "
|
|
21
|
-
- "
|
|
22
|
-
- "
|
|
21
|
+
- dictionaryType (string): Chemin dans la réponse (relatif à \`data\`). Exemples :
|
|
22
|
+
- "setting.state.{resource,candidate,contact,company,opportunity,project,invoice,order,positioning}" → états par entité
|
|
23
|
+
- "setting.typeOf.{resource,contact,project}" → types par entité
|
|
24
|
+
- "setting.action.{candidate,resource,opportunity,project,...}" → actions disponibles
|
|
25
|
+
- "setting.tool" → outils / technos (Java, AWS...)
|
|
26
|
+
- "setting.expertiseArea" → domaines d'expertise
|
|
27
|
+
- "setting.experience" → niveaux d'expérience
|
|
28
|
+
- "setting.languageSpoken" → langues parlées
|
|
29
|
+
- "setting.activityArea" → secteurs d'activité
|
|
30
|
+
- "setting.mobilityArea" → mobilités géographiques
|
|
31
|
+
- "setting.currency" → devises
|
|
32
|
+
- "setting.civility" → civilités
|
|
33
|
+
- "country" → pays
|
|
34
|
+
- "languages" → langues d'interface (fr, en, es)
|
|
35
|
+
|
|
36
|
+
Note : l'ancienne forme "states/resources" (slash) n'est pas valide — utilisez "setting.state.resource".
|
|
23
37
|
|
|
24
|
-
|
|
38
|
+
Retourne le sous-arbre (souvent un tableau \`{id, value, ...}\`) ou \`isError: true\` si le chemin est introuvable.`,
|
|
25
39
|
inputSchema: DictionaryGetSchema,
|
|
26
40
|
annotations: {
|
|
27
41
|
readOnlyHint: true,
|
|
@@ -30,10 +44,21 @@ Returns: Données du dictionnaire (clé/valeur).`,
|
|
|
30
44
|
openWorldHint: false,
|
|
31
45
|
},
|
|
32
46
|
}, async (params) => {
|
|
33
|
-
const
|
|
34
|
-
const
|
|
47
|
+
const { payload } = await getDictionary();
|
|
48
|
+
const node = resolveDictionaryPath(payload, params.dictionaryType);
|
|
49
|
+
if (node === undefined) {
|
|
50
|
+
return {
|
|
51
|
+
content: [
|
|
52
|
+
{
|
|
53
|
+
type: "text",
|
|
54
|
+
text: `Chemin "${params.dictionaryType}" introuvable dans le dictionnaire BoondManager. Les chemins sont relatifs à \`data\` (ex: "setting.state.resource", "country"). Note: "states/resources" (slash) n'est pas un chemin valide ; utilisez "setting.state.resource".`,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
isError: true,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
35
60
|
return {
|
|
36
|
-
content: [{ type: "text", text }],
|
|
61
|
+
content: [{ type: "text", text: formatDictionaryNode(node) }],
|
|
37
62
|
};
|
|
38
63
|
});
|
|
39
64
|
// Get current user / application info
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application.js","sourceRoot":"","sources":["../../src/tools/application.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"application.js","sourceRoot":"","sources":["../../src/tools/application.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEjF,SAAS,oBAAoB,CAAC,IAAa;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC3C,IAAI,IAAI,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,eAAe,CAAC,GAAG,2BAA2B,CAAC;IAC1E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,iBAAiB;IACjB,MAAM,CAAC,YAAY,CACjB,8BAA8B,EAC9B;QACE,KAAK,EAAE,wCAAwC;QAC/C,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;oHAsBiG;QAC9G,WAAW,EAAE,mBAAmB;QAChC,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,KAAK;SACrB;KACF,EACD,KAAK,EAAE,MAA0B,EAAE,EAAE;QACnC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,aAAa,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,qBAAqB,CAAC,OAAO,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;QACnE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,WAAW,MAAM,CAAC,cAAc,mOAAmO;qBAC1Q;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QACD,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;SACvE,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,sCAAsC;IACtC,MAAM,CAAC,YAAY,CACjB,gCAAgC,EAChC;QACE,KAAK,EAAE,kCAAkC;QACzC,WAAW,EAAE;;gDAE6B;QAC1C,WAAW,EAAE,EAAE;QACf,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,KAAK;YACtB,cAAc,EAAE,IAAI;YACpB,aAAa,EAAE,KAAK;SACrB;KACF,EACD,KAAK,IAAI,EAAE;QACT,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,2BAA2B,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAC5C,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC;SAC3C,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/manifest.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"manifest_version": "0.3",
|
|
3
3
|
"name": "boondmanager-mcp-server",
|
|
4
4
|
"display_name": "BoondManager MCP Server",
|
|
5
|
-
"version": "1.7.
|
|
6
|
-
"description": "MCP Server for BoondManager API - 156 tools, 11 prompts,
|
|
5
|
+
"version": "1.7.4",
|
|
6
|
+
"description": "MCP Server for BoondManager API - 156 tools, 11 prompts, 21 resources (ERP/CRM)",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Frédéric Auguste",
|
|
9
9
|
"url": "https://github.com/fauguste/boondmanager-mcp-server"
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
}
|
|
85
85
|
},
|
|
86
86
|
"tools_generated": true,
|
|
87
|
+
"prompts_generated": true,
|
|
87
88
|
"keywords": ["boondmanager", "esn", "crm", "erp", "candidates", "resources", "mcp"],
|
|
88
89
|
"compatibility": {
|
|
89
90
|
"platforms": ["darwin", "win32", "linux"],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "boondmanager-mcp-server",
|
|
3
|
-
"version": "1.7.
|
|
4
|
-
"description": "MCP Server for BoondManager API - 156 tools, 11 prompts,
|
|
3
|
+
"version": "1.7.4",
|
|
4
|
+
"description": "MCP Server for BoondManager API - 156 tools, 11 prompts, 21 resources (ERP/CRM)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "module",
|