@cencori/mcp 0.2.0

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/index.js ADDED
@@ -0,0 +1,955 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/index.ts
5
+ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+
7
+ // src/config.ts
8
+ var DEFAULT_BASE_URL = "https://cencori.com";
9
+ var KNOWN_FEATURES = [
10
+ "docs",
11
+ "gateway",
12
+ "agents",
13
+ "memory",
14
+ "sessions",
15
+ "multimodal",
16
+ "governance",
17
+ "guidance"
18
+ ];
19
+ function normalizeBaseUrl(value) {
20
+ return value.replace(/\/$/, "");
21
+ }
22
+ function parseBooleanEnv(value, defaultValue) {
23
+ if (value === void 0) {
24
+ return defaultValue;
25
+ }
26
+ return !["0", "false", "no", "off"].includes(value.trim().toLowerCase());
27
+ }
28
+ function isMcpFeature(value) {
29
+ return KNOWN_FEATURES.includes(value);
30
+ }
31
+ function allFeaturesEnabled() {
32
+ return {
33
+ docs: true,
34
+ gateway: true,
35
+ agents: true,
36
+ memory: true,
37
+ sessions: true,
38
+ multimodal: true,
39
+ governance: true,
40
+ guidance: true
41
+ };
42
+ }
43
+ function parseFeatures(value) {
44
+ if (!value || value.trim() === "") {
45
+ return allFeaturesEnabled();
46
+ }
47
+ const tokens = value.split(",").map((feature) => feature.trim().toLowerCase()).filter(Boolean);
48
+ const unrecognized = tokens.filter((token) => !isMcpFeature(token));
49
+ if (unrecognized.length > 0) {
50
+ console.error(
51
+ `[cencori-mcp] Ignoring unrecognized CENCORI_MCP_FEATURES value(s): ${unrecognized.join(", ")}. Known features: ${KNOWN_FEATURES.join(", ")}.`
52
+ );
53
+ }
54
+ const enabled = new Set(tokens.filter(isMcpFeature));
55
+ return {
56
+ docs: enabled.has("docs"),
57
+ gateway: enabled.has("gateway"),
58
+ agents: enabled.has("agents"),
59
+ memory: enabled.has("memory"),
60
+ sessions: enabled.has("sessions"),
61
+ multimodal: enabled.has("multimodal"),
62
+ governance: enabled.has("governance"),
63
+ guidance: enabled.has("guidance")
64
+ };
65
+ }
66
+ function loadConfig() {
67
+ const docsBaseUrl = normalizeBaseUrl(process.env.CENCORI_DOCS_BASE_URL ?? DEFAULT_BASE_URL);
68
+ const baseUrl = normalizeBaseUrl(process.env.CENCORI_BASE_URL ?? DEFAULT_BASE_URL);
69
+ const apiKey = process.env.CENCORI_API_KEY?.trim() || void 0;
70
+ const features = parseFeatures(process.env.CENCORI_MCP_FEATURES);
71
+ const destructive = parseBooleanEnv(process.env.CENCORI_MCP_DESTRUCTIVE, false);
72
+ const write = destructive || parseBooleanEnv(process.env.CENCORI_MCP_WRITE, false);
73
+ return {
74
+ docsBaseUrl,
75
+ baseUrl,
76
+ apiKey,
77
+ capabilities: { write, destructive },
78
+ features
79
+ };
80
+ }
81
+
82
+ // src/server.ts
83
+ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
84
+
85
+ // src/http.ts
86
+ var FETCH_TIMEOUT_MS = 15e3;
87
+ function fetchSignal() {
88
+ return AbortSignal.timeout(FETCH_TIMEOUT_MS);
89
+ }
90
+ async function readHttpErrorMessage(response) {
91
+ const errorData = await response.json().catch(() => null);
92
+ if (typeof errorData?.error === "string") {
93
+ return errorData.error;
94
+ }
95
+ if (errorData?.error && typeof errorData.error === "object" && errorData.error.message) {
96
+ return errorData.error.message;
97
+ }
98
+ return response.statusText || `HTTP ${response.status}`;
99
+ }
100
+
101
+ // src/client.ts
102
+ var PlatformClient = class {
103
+ constructor(baseUrl, apiKey) {
104
+ this.baseUrl = baseUrl;
105
+ this.apiKey = apiKey;
106
+ }
107
+ /**
108
+ * Low-level request against the Cencori API. Sends `Authorization: Bearer`,
109
+ * which the gateway accepts for both `/v1/*` and `/api/ai/*` routes.
110
+ */
111
+ async request(method, path, options = {}) {
112
+ const url = new URL(`/api${path}`, this.baseUrl);
113
+ if (options.searchParams) {
114
+ for (const [key, value] of Object.entries(options.searchParams)) {
115
+ if (value !== void 0) {
116
+ url.searchParams.set(key, value);
117
+ }
118
+ }
119
+ }
120
+ const headers = {
121
+ Authorization: `Bearer ${this.apiKey}`
122
+ };
123
+ const init = { method, headers, signal: fetchSignal() };
124
+ if (options.body !== void 0) {
125
+ headers["Content-Type"] = "application/json";
126
+ init.body = JSON.stringify(options.body);
127
+ }
128
+ const response = await fetch(url, init);
129
+ if (!response.ok) {
130
+ const message = await readHttpErrorMessage(response);
131
+ throw new Error(`Cencori API error: ${message}`);
132
+ }
133
+ const text = await response.text();
134
+ if (!text) {
135
+ return void 0;
136
+ }
137
+ return JSON.parse(text);
138
+ }
139
+ get(path, searchParams) {
140
+ return this.request("GET", path, { searchParams });
141
+ }
142
+ post(path, body, searchParams) {
143
+ return this.request("POST", path, { body, searchParams });
144
+ }
145
+ patch(path, body) {
146
+ return this.request("PATCH", path, { body });
147
+ }
148
+ del(path) {
149
+ return this.request("DELETE", path);
150
+ }
151
+ listModels() {
152
+ return this.get("/v1/models");
153
+ }
154
+ getMetrics(period = "7d") {
155
+ return this.get("/v1/metrics", { period });
156
+ }
157
+ listAgents() {
158
+ return this.get("/v1/agents");
159
+ }
160
+ getAgent(agentId) {
161
+ return this.get(`/v1/agents/${agentId}`);
162
+ }
163
+ };
164
+
165
+ // src/docs/client.ts
166
+ var DocsClient = class {
167
+ constructor(baseUrl) {
168
+ this.baseUrl = baseUrl;
169
+ }
170
+ async search(query) {
171
+ const url = new URL("/api/docs/search", this.baseUrl);
172
+ url.searchParams.set("q", query);
173
+ const response = await fetch(url, { signal: fetchSignal() });
174
+ if (!response.ok) {
175
+ const message = await readHttpErrorMessage(response);
176
+ throw new Error(`Docs search failed (${response.status}): ${message}`);
177
+ }
178
+ return response.json();
179
+ }
180
+ async getDoc(slug) {
181
+ const normalizedSlug = slug.replace(/^\/docs\//, "").replace(/^\//, "");
182
+ const url = new URL("/api/docs/raw", this.baseUrl);
183
+ url.searchParams.set("slug", normalizedSlug);
184
+ const response = await fetch(url, { signal: fetchSignal() });
185
+ if (response.status === 404) {
186
+ return { content: "", error: `Document not found: ${normalizedSlug}` };
187
+ }
188
+ if (!response.ok) {
189
+ const message = await readHttpErrorMessage(response);
190
+ throw new Error(`Docs fetch failed (${response.status}): ${message}`);
191
+ }
192
+ return response.json();
193
+ }
194
+ async listNavigation() {
195
+ const url = new URL("/api/docs/navigation", this.baseUrl);
196
+ const response = await fetch(url, { signal: fetchSignal() });
197
+ if (!response.ok) {
198
+ const message = await readHttpErrorMessage(response);
199
+ throw new Error(`Docs navigation failed (${response.status}): ${message}`);
200
+ }
201
+ return response.json();
202
+ }
203
+ };
204
+
205
+ // src/tools/docs.ts
206
+ var import_zod = require("zod");
207
+
208
+ // src/tools/shared.ts
209
+ var READ_ONLY_ANNOTATIONS = {
210
+ title: "Read-only",
211
+ readOnlyHint: true,
212
+ destructiveHint: false,
213
+ openWorldHint: false
214
+ };
215
+ var WRITE_ANNOTATIONS = {
216
+ title: "Write",
217
+ readOnlyHint: false,
218
+ destructiveHint: false,
219
+ openWorldHint: false
220
+ };
221
+ function jsonResult(data) {
222
+ return {
223
+ content: [
224
+ {
225
+ type: "text",
226
+ text: JSON.stringify(data, null, 2)
227
+ }
228
+ ]
229
+ };
230
+ }
231
+
232
+ // src/tools/docs.ts
233
+ function registerDocsTools(server, docs, docsBaseUrl) {
234
+ server.registerTool(
235
+ "search_docs",
236
+ {
237
+ title: "Search Cencori docs",
238
+ description: "Search Cencori documentation by keyword. Returns matching pages with title, section, URL, and snippet.",
239
+ inputSchema: {
240
+ query: import_zod.z.string().min(2).describe('Search query (minimum 2 characters). Example: "rate limiting", "api keys", "failover".')
241
+ },
242
+ annotations: READ_ONLY_ANNOTATIONS
243
+ },
244
+ async ({ query }) => {
245
+ const { results } = await docs.search(query);
246
+ return jsonResult({ query, count: results.length, results });
247
+ }
248
+ );
249
+ server.registerTool(
250
+ "get_doc",
251
+ {
252
+ title: "Get Cencori doc page",
253
+ description: 'Fetch the raw markdown content of a Cencori documentation page by slug. Example slugs: "quick-start", "ai/sdk", "api/chat".',
254
+ inputSchema: {
255
+ slug: import_zod.z.string().min(1).describe('Doc slug without /docs/ prefix. Example: "ai/gateway" or "security/scan".')
256
+ },
257
+ annotations: READ_ONLY_ANNOTATIONS
258
+ },
259
+ async ({ slug }) => {
260
+ const doc = await docs.getDoc(slug);
261
+ const normalizedSlug = slug.replace(/^\/docs\//, "").replace(/^\//, "");
262
+ if (doc.error) {
263
+ return jsonResult({ slug: normalizedSlug, error: doc.error });
264
+ }
265
+ return jsonResult({
266
+ slug: normalizedSlug,
267
+ url: `${docsBaseUrl}/docs/${normalizedSlug}`,
268
+ content: doc.content
269
+ });
270
+ }
271
+ );
272
+ server.registerTool(
273
+ "list_docs",
274
+ {
275
+ title: "List Cencori docs",
276
+ description: "List the Cencori documentation table of contents grouped by section (Getting Started, Platform, AI, API Reference, etc.).",
277
+ inputSchema: {},
278
+ annotations: READ_ONLY_ANNOTATIONS
279
+ },
280
+ async () => {
281
+ const navigation = await docs.listNavigation();
282
+ return jsonResult(navigation);
283
+ }
284
+ );
285
+ }
286
+
287
+ // src/tools/gateway.ts
288
+ var import_zod2 = require("zod");
289
+ function registerGatewayTools(server, client) {
290
+ server.registerTool(
291
+ "list_models",
292
+ {
293
+ title: "List Cencori gateway models",
294
+ description: "List models available through the Cencori gateway for the authenticated project.",
295
+ inputSchema: {},
296
+ annotations: READ_ONLY_ANNOTATIONS
297
+ },
298
+ async () => {
299
+ const models = await client.listModels();
300
+ return jsonResult(models);
301
+ }
302
+ );
303
+ server.registerTool(
304
+ "get_metrics",
305
+ {
306
+ title: "Get Cencori gateway metrics",
307
+ description: "Get gateway usage metrics for the authenticated project, including requests, cost, tokens, latency, and model/provider breakdowns.",
308
+ inputSchema: {
309
+ period: import_zod2.z.enum(["1h", "24h", "7d", "30d", "mtd"]).optional().describe("Metrics time window. Allowed values: 1h, 24h, 7d, 30d, mtd.")
310
+ },
311
+ annotations: READ_ONLY_ANNOTATIONS
312
+ },
313
+ async ({ period }) => {
314
+ const metrics = await client.getMetrics(period);
315
+ return jsonResult(metrics);
316
+ }
317
+ );
318
+ server.registerTool(
319
+ "get_health",
320
+ {
321
+ title: "Get Cencori gateway health",
322
+ description: "Check gateway/platform health for the authenticated project.",
323
+ inputSchema: {},
324
+ annotations: READ_ONLY_ANNOTATIONS
325
+ },
326
+ async () => jsonResult(await client.get("/v1/health"))
327
+ );
328
+ server.registerTool(
329
+ "check_quota",
330
+ {
331
+ title: "Check billing quota",
332
+ description: "Check remaining quota/credits and usage limits for the authenticated project.",
333
+ inputSchema: {},
334
+ annotations: READ_ONLY_ANNOTATIONS
335
+ },
336
+ async () => jsonResult(await client.get("/v1/billing/check-quota"))
337
+ );
338
+ }
339
+
340
+ // src/tools/agents.ts
341
+ var import_zod3 = require("zod");
342
+ function registerAgentsTools(server, client) {
343
+ server.registerTool(
344
+ "list_agents",
345
+ {
346
+ title: "List Cencori agents",
347
+ description: "List agents available to the authenticated project.",
348
+ inputSchema: {},
349
+ annotations: READ_ONLY_ANNOTATIONS
350
+ },
351
+ async () => {
352
+ const agents = await client.listAgents();
353
+ return jsonResult(agents);
354
+ }
355
+ );
356
+ server.registerTool(
357
+ "get_agent",
358
+ {
359
+ title: "Get Cencori agent",
360
+ description: "Fetch the full configuration for one Cencori agent by ID.",
361
+ inputSchema: {
362
+ agent_id: import_zod3.z.string().min(1).describe("The agent ID to fetch.")
363
+ },
364
+ annotations: READ_ONLY_ANNOTATIONS
365
+ },
366
+ async ({ agent_id }) => {
367
+ const agent = await client.getAgent(agent_id);
368
+ return jsonResult(agent);
369
+ }
370
+ );
371
+ server.registerTool(
372
+ "poll_agent_actions",
373
+ {
374
+ title: "Poll pending agent actions",
375
+ description: "Poll for pending actions queued for agents in the authenticated project.",
376
+ inputSchema: {},
377
+ annotations: READ_ONLY_ANNOTATIONS
378
+ },
379
+ async () => jsonResult(await client.get("/v1/agent/actions/poll"))
380
+ );
381
+ }
382
+
383
+ // src/tools/memory.ts
384
+ var import_zod4 = require("zod");
385
+ var scopeShape = {
386
+ namespace: import_zod4.z.string().optional().describe("Memory namespace to scope to."),
387
+ scope: import_zod4.z.string().optional().describe("Memory scope (e.g. project, user, session)."),
388
+ user_id: import_zod4.z.string().optional().describe("Scope to a specific end-user id."),
389
+ session_id: import_zod4.z.string().optional().describe("Scope to a specific session id.")
390
+ };
391
+ function registerMemoryTools(server, client) {
392
+ server.registerTool(
393
+ "list_memories",
394
+ {
395
+ title: "List memories",
396
+ description: "List stored memories for the project, optionally scoped by namespace/user/session.",
397
+ inputSchema: {
398
+ ...scopeShape,
399
+ limit: import_zod4.z.number().int().positive().max(200).optional(),
400
+ cursor: import_zod4.z.string().optional().describe("Pagination cursor from a previous response.")
401
+ },
402
+ annotations: READ_ONLY_ANNOTATIONS
403
+ },
404
+ async ({ namespace, scope, user_id, session_id, limit, cursor }) => jsonResult(
405
+ await client.get("/v1/memory/list", {
406
+ namespace,
407
+ scope,
408
+ userId: user_id,
409
+ sessionId: session_id,
410
+ limit: limit?.toString(),
411
+ cursor
412
+ })
413
+ )
414
+ );
415
+ server.registerTool(
416
+ "search_memory",
417
+ {
418
+ title: "Search memory (semantic)",
419
+ description: "Semantically search stored memories by query.",
420
+ inputSchema: {
421
+ query: import_zod4.z.string().min(1).describe("Natural-language search query."),
422
+ ...scopeShape,
423
+ top_k: import_zod4.z.number().int().positive().max(100).optional().describe("Max results."),
424
+ threshold: import_zod4.z.number().min(0).max(1).optional().describe("Similarity threshold.")
425
+ },
426
+ annotations: READ_ONLY_ANNOTATIONS
427
+ },
428
+ async ({ query, namespace, scope, user_id, session_id, top_k, threshold }) => jsonResult(
429
+ await client.post("/v1/memory/search", {
430
+ query,
431
+ namespace,
432
+ scope,
433
+ userId: user_id,
434
+ sessionId: session_id,
435
+ topK: top_k,
436
+ threshold
437
+ })
438
+ )
439
+ );
440
+ server.registerTool(
441
+ "get_memory",
442
+ {
443
+ title: "Get a memory",
444
+ description: "Fetch a single memory by id.",
445
+ inputSchema: { memory_id: import_zod4.z.string().min(1).describe("The memory id.") },
446
+ annotations: READ_ONLY_ANNOTATIONS
447
+ },
448
+ async ({ memory_id }) => jsonResult(await client.get(`/v1/memory/${memory_id}`))
449
+ );
450
+ server.registerTool(
451
+ "list_memory_entities",
452
+ {
453
+ title: "List memory entities",
454
+ description: "List entities resolved from the memory graph.",
455
+ inputSchema: scopeShape,
456
+ annotations: READ_ONLY_ANNOTATIONS
457
+ },
458
+ async ({ namespace, scope, user_id, session_id }) => jsonResult(
459
+ await client.get("/v1/memory/entities", {
460
+ namespace,
461
+ scope,
462
+ userId: user_id,
463
+ sessionId: session_id
464
+ })
465
+ )
466
+ );
467
+ server.registerTool(
468
+ "get_memory_graph",
469
+ {
470
+ title: "Get memory entity graph",
471
+ description: "Traverse the memory entity graph from an optional starting entity.",
472
+ inputSchema: {
473
+ ...scopeShape,
474
+ entity: import_zod4.z.string().optional().describe("Entity to start traversal from."),
475
+ hops: import_zod4.z.number().int().positive().max(5).optional().describe("Traversal depth.")
476
+ },
477
+ annotations: READ_ONLY_ANNOTATIONS
478
+ },
479
+ async ({ namespace, scope, user_id, session_id, entity, hops }) => jsonResult(
480
+ await client.get("/v1/memory/graph", {
481
+ namespace,
482
+ scope,
483
+ userId: user_id,
484
+ sessionId: session_id,
485
+ entity,
486
+ hops: hops?.toString()
487
+ })
488
+ )
489
+ );
490
+ server.registerTool(
491
+ "get_forget_suggestions",
492
+ {
493
+ title: "Get forget suggestions",
494
+ description: "List memories the system suggests forgetting (stale/superseded).",
495
+ inputSchema: scopeShape,
496
+ annotations: READ_ONLY_ANNOTATIONS
497
+ },
498
+ async ({ namespace, scope, user_id, session_id }) => jsonResult(
499
+ await client.get("/v1/memory/forget-suggestions", {
500
+ namespace,
501
+ scope,
502
+ userId: user_id,
503
+ sessionId: session_id
504
+ })
505
+ )
506
+ );
507
+ }
508
+
509
+ // src/tools/sessions.ts
510
+ var import_zod5 = require("zod");
511
+ function registerSessionsTools(server, client) {
512
+ server.registerTool(
513
+ "list_sessions",
514
+ {
515
+ title: "List agent sessions",
516
+ description: "List agent sessions for the project, optionally filtered by status or agent.",
517
+ inputSchema: {
518
+ status: import_zod5.z.string().optional().describe("Filter by session status."),
519
+ agent_id: import_zod5.z.string().optional().describe("Filter by agent id."),
520
+ page: import_zod5.z.number().int().positive().optional(),
521
+ limit: import_zod5.z.number().int().positive().max(100).optional()
522
+ },
523
+ annotations: READ_ONLY_ANNOTATIONS
524
+ },
525
+ async ({ status, agent_id, page, limit }) => jsonResult(
526
+ await client.get("/v1/sessions", {
527
+ status,
528
+ agent_id,
529
+ page: page?.toString(),
530
+ limit: limit?.toString()
531
+ })
532
+ )
533
+ );
534
+ server.registerTool(
535
+ "get_session",
536
+ {
537
+ title: "Get an agent session",
538
+ description: "Fetch one agent session by id.",
539
+ inputSchema: { session_id: import_zod5.z.string().min(1).describe("The session id.") },
540
+ annotations: READ_ONLY_ANNOTATIONS
541
+ },
542
+ async ({ session_id }) => jsonResult(await client.get(`/v1/sessions/${session_id}`))
543
+ );
544
+ server.registerTool(
545
+ "get_session_events",
546
+ {
547
+ title: "Get agent session events",
548
+ description: "List the event timeline for one agent session.",
549
+ inputSchema: { session_id: import_zod5.z.string().min(1).describe("The session id.") },
550
+ annotations: READ_ONLY_ANNOTATIONS
551
+ },
552
+ async ({ session_id }) => jsonResult(await client.get(`/v1/sessions/${session_id}/events`))
553
+ );
554
+ }
555
+
556
+ // src/tools/governance.ts
557
+ var import_zod6 = require("zod");
558
+ function registerGovernanceTools(server, client) {
559
+ server.registerTool(
560
+ "list_policies",
561
+ {
562
+ title: "List governance policies",
563
+ description: "List governance policies for the org, optionally filtered by status.",
564
+ inputSchema: {
565
+ status: import_zod6.z.enum(["draft", "pending_review", "active", "retired"]).optional()
566
+ },
567
+ annotations: READ_ONLY_ANNOTATIONS
568
+ },
569
+ async ({ status }) => jsonResult(await client.get("/v1/governance/policies", { status }))
570
+ );
571
+ server.registerTool(
572
+ "list_roles",
573
+ {
574
+ title: "List governance roles",
575
+ description: "List governance roles defined for the org.",
576
+ inputSchema: {},
577
+ annotations: READ_ONLY_ANNOTATIONS
578
+ },
579
+ async () => jsonResult(await client.get("/v1/governance/roles"))
580
+ );
581
+ server.registerTool(
582
+ "list_change_requests",
583
+ {
584
+ title: "List governance change requests",
585
+ description: "List maker-checker change requests for governance policies.",
586
+ inputSchema: {},
587
+ annotations: READ_ONLY_ANNOTATIONS
588
+ },
589
+ async () => jsonResult(await client.get("/v1/governance/change-requests"))
590
+ );
591
+ server.registerTool(
592
+ "get_governance_ledger",
593
+ {
594
+ title: "Get governance audit ledger",
595
+ description: "Read the immutable governance audit ledger.",
596
+ inputSchema: {},
597
+ annotations: READ_ONLY_ANNOTATIONS
598
+ },
599
+ async () => jsonResult(await client.get("/v1/governance/ledger"))
600
+ );
601
+ server.registerTool(
602
+ "get_governance_evidence",
603
+ {
604
+ title: "Get governance evidence",
605
+ description: "Read governance evidence records (enforcement decisions).",
606
+ inputSchema: {},
607
+ annotations: READ_ONLY_ANNOTATIONS
608
+ },
609
+ async () => jsonResult(await client.get("/v1/governance/evidence"))
610
+ );
611
+ server.registerTool(
612
+ "list_governance_templates",
613
+ {
614
+ title: "List governance policy templates",
615
+ description: "List installable governance policy templates.",
616
+ inputSchema: {},
617
+ annotations: READ_ONLY_ANNOTATIONS
618
+ },
619
+ async () => jsonResult(await client.get("/v1/governance/templates"))
620
+ );
621
+ }
622
+
623
+ // src/tools/multimodal.ts
624
+ var import_zod7 = require("zod");
625
+ var messageSchema = import_zod7.z.array(
626
+ import_zod7.z.object({
627
+ role: import_zod7.z.enum(["system", "user", "assistant"]),
628
+ content: import_zod7.z.string()
629
+ })
630
+ ).min(1).describe("Chat messages in OpenAI format.");
631
+ var imageSourceShape = {
632
+ image_url: import_zod7.z.string().url().optional().describe("Public URL of the image."),
633
+ image_base64: import_zod7.z.string().optional().describe("Base64-encoded image bytes (use with mime_type)."),
634
+ mime_type: import_zod7.z.string().optional().describe("MIME type for image_base64, e.g. image/png."),
635
+ prompt: import_zod7.z.string().optional().describe("Optional instruction to steer the analysis."),
636
+ model: import_zod7.z.string().optional().describe("Override the model.")
637
+ };
638
+ var documentSourceShape = {
639
+ document_url: import_zod7.z.string().url().optional().describe("Public URL of the PDF/image."),
640
+ document_base64: import_zod7.z.string().optional().describe("Base64-encoded document bytes (use with mime_type)."),
641
+ mime_type: import_zod7.z.string().optional().describe("MIME type for document_base64, e.g. application/pdf."),
642
+ model: import_zod7.z.string().optional().describe("Override the model.")
643
+ };
644
+ function registerMultimodalTools(server, client) {
645
+ server.registerTool(
646
+ "generate_text",
647
+ {
648
+ title: "Generate text (chat completion)",
649
+ description: "Run a chat completion through the Cencori gateway. Incurs usage/cost.",
650
+ inputSchema: {
651
+ model: import_zod7.z.string().describe("Model id, e.g. llama-3.1-8b-instant or claude-opus-5."),
652
+ messages: messageSchema,
653
+ temperature: import_zod7.z.number().min(0).max(2).optional(),
654
+ max_tokens: import_zod7.z.number().int().positive().optional()
655
+ },
656
+ annotations: WRITE_ANNOTATIONS
657
+ },
658
+ async ({ model, messages, temperature, max_tokens }) => jsonResult(await client.post("/v1/chat/completions", { model, messages, temperature, max_tokens }))
659
+ );
660
+ server.registerTool(
661
+ "generate_rag",
662
+ {
663
+ title: "RAG chat over a memory namespace",
664
+ description: "Answer a question grounded in stored memories for a namespace. Incurs usage/cost.",
665
+ inputSchema: {
666
+ model: import_zod7.z.string().describe("Model id for the answer."),
667
+ messages: messageSchema,
668
+ namespace: import_zod7.z.string().min(1).describe("Memory namespace to retrieve context from."),
669
+ limit: import_zod7.z.number().int().positive().optional().describe("Max memories to retrieve.")
670
+ },
671
+ annotations: WRITE_ANNOTATIONS
672
+ },
673
+ async ({ model, messages, namespace, limit }) => jsonResult(await client.post("/ai/rag", { model, messages, namespace, limit }))
674
+ );
675
+ server.registerTool(
676
+ "create_embeddings",
677
+ {
678
+ title: "Create embeddings",
679
+ description: "Generate vector embeddings for text. Incurs usage/cost.",
680
+ inputSchema: {
681
+ input: import_zod7.z.union([import_zod7.z.string(), import_zod7.z.array(import_zod7.z.string())]).describe("Text or array of texts to embed."),
682
+ model: import_zod7.z.string().optional().describe("Embedding model. Defaults to text-embedding-3-small."),
683
+ dimensions: import_zod7.z.number().int().positive().optional()
684
+ },
685
+ annotations: WRITE_ANNOTATIONS
686
+ },
687
+ async ({ input, model, dimensions }) => jsonResult(await client.post("/ai/embeddings", { input, model, dimensions }))
688
+ );
689
+ server.registerTool(
690
+ "moderate_content",
691
+ {
692
+ title: "Moderate content",
693
+ description: "Classify text for policy violations via the moderation endpoint.",
694
+ inputSchema: {
695
+ input: import_zod7.z.union([import_zod7.z.string(), import_zod7.z.array(import_zod7.z.string())]).describe("Text or array of texts to moderate."),
696
+ model: import_zod7.z.string().optional()
697
+ },
698
+ annotations: WRITE_ANNOTATIONS
699
+ },
700
+ async ({ input, model }) => jsonResult(await client.post("/ai/moderation", { input, model }))
701
+ );
702
+ server.registerTool(
703
+ "generate_image",
704
+ {
705
+ title: "Generate an image",
706
+ description: "Generate image(s) from a text prompt. Incurs usage/cost.",
707
+ inputSchema: {
708
+ prompt: import_zod7.z.string().min(1).describe("Text prompt describing the image."),
709
+ model: import_zod7.z.string().optional(),
710
+ n: import_zod7.z.number().int().positive().optional().describe("Number of images."),
711
+ size: import_zod7.z.string().optional().describe("Image size, e.g. 1024x1024.")
712
+ },
713
+ annotations: WRITE_ANNOTATIONS
714
+ },
715
+ async ({ prompt, model, n, size }) => jsonResult(await client.post("/ai/images/generate", { prompt, model, n, size }))
716
+ );
717
+ for (const [name, path, title] of [
718
+ ["describe_image", "/ai/vision/describe", "Describe an image"],
719
+ ["ocr_image", "/ai/vision/ocr", "Extract text from an image (OCR)"],
720
+ ["classify_image", "/ai/vision/classify", "Classify an image (JSON tags)"]
721
+ ]) {
722
+ server.registerTool(
723
+ name,
724
+ {
725
+ title,
726
+ description: `${title} via the Cencori vision endpoint. Incurs usage/cost.`,
727
+ inputSchema: imageSourceShape,
728
+ annotations: WRITE_ANNOTATIONS
729
+ },
730
+ async (args) => jsonResult(await client.post(path, args))
731
+ );
732
+ }
733
+ server.registerTool(
734
+ "extract_document",
735
+ {
736
+ title: "Extract text from a document",
737
+ description: "Extract clean text from a PDF or image (native extraction for text PDFs). Incurs usage/cost for OCR.",
738
+ inputSchema: documentSourceShape,
739
+ annotations: WRITE_ANNOTATIONS
740
+ },
741
+ async (args) => jsonResult(await client.post("/ai/documents/extract", args))
742
+ );
743
+ server.registerTool(
744
+ "summarize_document",
745
+ {
746
+ title: "Summarize a document",
747
+ description: "Extract and summarize a PDF or image. Incurs usage/cost.",
748
+ inputSchema: { ...documentSourceShape, prompt: import_zod7.z.string().optional().describe("Optional summary instruction.") },
749
+ annotations: WRITE_ANNOTATIONS
750
+ },
751
+ async (args) => jsonResult(await client.post("/ai/documents/summarize", args))
752
+ );
753
+ server.registerTool(
754
+ "query_document",
755
+ {
756
+ title: "Ask a question about a document",
757
+ description: "Extract a document and answer a question about it. Incurs usage/cost.",
758
+ inputSchema: {
759
+ ...documentSourceShape,
760
+ question: import_zod7.z.string().min(1).describe("The question to answer about the document.")
761
+ },
762
+ annotations: WRITE_ANNOTATIONS
763
+ },
764
+ async (args) => jsonResult(await client.post("/ai/documents/query", args))
765
+ );
766
+ }
767
+
768
+ // src/tools/guidance.ts
769
+ var import_zod8 = require("zod");
770
+ var orgProjectShape = {
771
+ org_slug: import_zod8.z.string().min(1).optional().describe("Your organization slug, to build an exact dashboard link."),
772
+ project_slug: import_zod8.z.string().min(1).optional().describe("Your project slug, to build an exact dashboard link.")
773
+ };
774
+ function registerGuide(server, name, title, description, buildPath, steps, baseUrl) {
775
+ server.registerTool(
776
+ name,
777
+ {
778
+ title,
779
+ description,
780
+ inputSchema: orgProjectShape,
781
+ annotations: READ_ONLY_ANNOTATIONS
782
+ },
783
+ async (args) => {
784
+ return jsonResult({
785
+ manual_action: true,
786
+ note: "This action is intentionally not automated. Tell the user to complete it themselves in the Cencori dashboard.",
787
+ steps,
788
+ dashboard_url: `${baseUrl}${buildPath(args)}`
789
+ });
790
+ }
791
+ );
792
+ }
793
+ var ORG = (s) => s.org_slug ?? "<your-org>";
794
+ var PROJ = (s) => s.project_slug ?? "<your-project>";
795
+ function registerGuidanceTools(server, baseUrl) {
796
+ registerGuide(
797
+ server,
798
+ "how_to_create_api_key",
799
+ "How to create a Cencori API key",
800
+ "Steps for the USER to create a Cencori project API key. The MCP never creates keys.",
801
+ (s) => `/${ORG(s)}/${PROJ(s)}/api-keys`,
802
+ [
803
+ "Open the Cencori dashboard and select your organization, then your project.",
804
+ "Go to the project\u2019s API Keys page.",
805
+ "Click \u201CCreate key\u201D, name it, choose the environment, and copy the `csk_...` secret.",
806
+ "Store it server-side as CENCORI_API_KEY \u2014 it is shown only once."
807
+ ],
808
+ baseUrl
809
+ );
810
+ registerGuide(
811
+ server,
812
+ "how_to_edit_api_key",
813
+ "How to edit a Cencori API key",
814
+ "Steps for the USER to rename or change scopes/limits on an API key. The MCP never edits keys.",
815
+ (s) => `/${ORG(s)}/${PROJ(s)}/api-keys`,
816
+ [
817
+ "Open the project\u2019s API Keys page in the dashboard.",
818
+ "Find the key and open its actions menu.",
819
+ "Update its name, environment, or limits and save."
820
+ ],
821
+ baseUrl
822
+ );
823
+ registerGuide(
824
+ server,
825
+ "how_to_revoke_api_key",
826
+ "How to revoke a Cencori API key",
827
+ "Steps for the USER to revoke/rotate an API key. The MCP never revokes keys.",
828
+ (s) => `/${ORG(s)}/${PROJ(s)}/api-keys`,
829
+ [
830
+ "Open the project\u2019s API Keys page in the dashboard.",
831
+ "Find the key to revoke and choose \u201CRevoke\u201D. This immediately invalidates it.",
832
+ "If rotating, create a new key first and update CENCORI_API_KEY before revoking the old one."
833
+ ],
834
+ baseUrl
835
+ );
836
+ registerGuide(
837
+ server,
838
+ "how_to_activate_policy",
839
+ "How to activate a governance policy",
840
+ "Steps for the USER to activate/retire a governance policy. Activation is a maker-checker human step \u2014 the MCP can draft a policy but never activates one.",
841
+ (s) => `/${ORG(s)}/~/governance`,
842
+ [
843
+ "Open the Governance page for your organization in the dashboard.",
844
+ "Find the draft (or pending-review) policy version.",
845
+ "Review it and, as an authorized checker, activate it. Exactly one version per policy can be active."
846
+ ],
847
+ baseUrl
848
+ );
849
+ registerGuide(
850
+ server,
851
+ "how_to_respond_to_change_request",
852
+ "How to respond to a governance change request",
853
+ "Steps for the USER (as checker) to approve or reject a governance change request. The MCP never approves/rejects on the user\u2019s behalf.",
854
+ (s) => `/${ORG(s)}/~/governance`,
855
+ [
856
+ "Open the Governance page and go to Change Requests.",
857
+ "Open the pending request and review the proposed change.",
858
+ "As an authorized checker, approve or reject it."
859
+ ],
860
+ baseUrl
861
+ );
862
+ registerGuide(
863
+ server,
864
+ "how_to_change_plan",
865
+ "How to change your Cencori plan",
866
+ "Steps for the USER to change plan/tier. The MCP never changes plans or charges.",
867
+ (s) => `/${ORG(s)}/~/billing`,
868
+ [
869
+ "Open the Billing page for your organization in the dashboard.",
870
+ "Choose the plan you want and confirm the change."
871
+ ],
872
+ baseUrl
873
+ );
874
+ registerGuide(
875
+ server,
876
+ "how_to_manage_billing",
877
+ "How to manage billing, payment methods, and credits",
878
+ "Steps for the USER to update payment methods or buy credits. The MCP never manages payment or credits.",
879
+ (s) => `/${ORG(s)}/~/billing`,
880
+ [
881
+ "Open the Billing page for your organization in the dashboard.",
882
+ "Update a payment method, or purchase credits, from the billing controls."
883
+ ],
884
+ baseUrl
885
+ );
886
+ registerGuide(
887
+ server,
888
+ "how_to_manage_members",
889
+ "How to manage members, roles, and SSO",
890
+ "Steps for the USER to invite/remove members, change roles, or configure SSO. The MCP never alters access.",
891
+ (s) => `/${ORG(s)}/~/teams`,
892
+ [
893
+ "Open the Teams page for your organization in the dashboard.",
894
+ "Invite or remove members, or change a member\u2019s role.",
895
+ "Configure SSO from the organization settings if applicable."
896
+ ],
897
+ baseUrl
898
+ );
899
+ }
900
+
901
+ // src/server.ts
902
+ var SERVER_NAME = "cencori";
903
+ var SERVER_VERSION = "0.2.0";
904
+ function createServer(config) {
905
+ const server = new import_mcp.McpServer(
906
+ {
907
+ name: SERVER_NAME,
908
+ version: SERVER_VERSION,
909
+ websiteUrl: "https://cencori.com/docs"
910
+ },
911
+ {
912
+ capabilities: {
913
+ tools: {}
914
+ }
915
+ }
916
+ );
917
+ const { features, capabilities } = config;
918
+ if (features.docs) {
919
+ const docs = new DocsClient(config.docsBaseUrl);
920
+ registerDocsTools(server, docs, config.docsBaseUrl);
921
+ }
922
+ if (features.guidance) {
923
+ registerGuidanceTools(server, config.baseUrl);
924
+ }
925
+ if (config.apiKey) {
926
+ const client = new PlatformClient(config.baseUrl, config.apiKey);
927
+ if (features.gateway) registerGatewayTools(server, client);
928
+ if (features.agents) registerAgentsTools(server, client);
929
+ if (features.memory) registerMemoryTools(server, client);
930
+ if (features.sessions) registerSessionsTools(server, client);
931
+ if (features.governance) registerGovernanceTools(server, client);
932
+ if (features.multimodal && capabilities.write) {
933
+ registerMultimodalTools(server, client);
934
+ }
935
+ }
936
+ return server;
937
+ }
938
+
939
+ // src/index.ts
940
+ async function main() {
941
+ const config = loadConfig();
942
+ const server = createServer(config);
943
+ const transport = new import_stdio.StdioServerTransport();
944
+ await server.connect(transport);
945
+ const enabledFeatures = Object.entries(config.features).filter(([, enabled]) => enabled).map(([feature]) => feature).join(", ");
946
+ const tier = config.capabilities.destructive ? "read+write+destructive" : config.capabilities.write ? "read+write" : "read-only";
947
+ console.error(
948
+ `Cencori MCP server running (tier=${tier}, base=${config.baseUrl}, features=${enabledFeatures}, apiKey=${config.apiKey ? "configured" : "not set"})`
949
+ );
950
+ }
951
+ main().catch((error) => {
952
+ console.error("Fatal error:", error);
953
+ process.exit(1);
954
+ });
955
+ //# sourceMappingURL=index.js.map