@cencori/mcp 0.2.0 → 0.5.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/README.md CHANGED
@@ -42,6 +42,7 @@ Docs + `how_to_*` guidance tools work with **no API key**. Add `CENCORI_API_KEY`
42
42
  | Tool | Description |
43
43
  |------|-------------|
44
44
  | `search_docs` / `get_doc` / `list_docs` | Search / fetch / list Cencori documentation |
45
+ | `get_integration_guide` | Fetch the full `llm.txt` integration contract — the authoritative "how to set up Cencori in a codebase" guide |
45
46
  | `how_to_create_api_key`, `how_to_edit_api_key`, `how_to_revoke_api_key` | Guidance: API-key lifecycle (manual) |
46
47
  | `how_to_activate_policy`, `how_to_respond_to_change_request` | Guidance: governance checker steps (manual) |
47
48
  | `how_to_change_plan`, `how_to_manage_billing` | Guidance: billing / plan / credits (manual) |
@@ -57,11 +58,19 @@ Docs + `how_to_*` guidance tools work with **no API key**. Add `CENCORI_API_KEY`
57
58
  | Sessions | `list_sessions`, `get_session`, `get_session_events` |
58
59
  | Governance | `list_policies`, `list_roles`, `list_change_requests`, `get_governance_ledger`, `get_governance_evidence`, `list_governance_templates` |
59
60
 
60
- ### Inference (Write-tier) — requires `CENCORI_MCP_WRITE=1`
61
+ ### Write — requires `CENCORI_MCP_WRITE=1`
61
62
 
62
- `generate_text`, `generate_rag`, `create_embeddings`, `moderate_content`, `generate_image`, `describe_image`, `ocr_image`, `classify_image`, `extract_document`, `summarize_document`, `query_document`.
63
+ - **Inference:** `generate_text`, `generate_rag`, `create_embeddings`, `moderate_content`, `generate_image`, `describe_image`, `ocr_image`, `classify_image`, `extract_document`, `summarize_document`, `query_document`.
64
+ - **Memory:** `remember_memory`, `write_memory`, `create_namespace`.
65
+ - **Agents:** `create_agent`, `update_agent`.
66
+ - **Sessions:** `create_session`, `add_session_turn`.
67
+ - **Governance (draft only):** `create_policy`, `install_template`. Policies are created as **drafts** — activation stays a manual human step (`how_to_activate_policy`).
63
68
 
64
- > Roadmap: Phase 2 adds memory/agent/session write + destructive tools; Phase 3 adds governance policy *drafting*. Audio (TTS/STT) is planned once binary/multipart transport lands.
69
+ ### Destructive requires `CENCORI_MCP_DESTRUCTIVE=1` (implies write)
70
+
71
+ `delete_memory`, `delete_agent`, `delete_session`, `approve_session`, `reject_session`. All carry `destructiveHint` so clients can confirm.
72
+
73
+ > Roadmap: audio (TTS/STT) once binary/multipart transport lands.
65
74
 
66
75
  ---
67
76
 
@@ -122,11 +131,12 @@ See [`mcp.example.json`](./mcp.example.json) for a copy-paste template.
122
131
 
123
132
  ### Behavior
124
133
 
125
- | `CENCORI_API_KEY` | `CENCORI_MCP_WRITE` | Registered |
126
- |-------------------|---------------------|-----------|
134
+ | `CENCORI_API_KEY` | Flag | Registered |
135
+ |-------------------|------|-----------|
127
136
  | unset | — | docs + `how_to_*` guidance only |
128
- | set | unset | guidance + all **reads** |
129
- | set | `1` | guidance + reads + **inference** |
137
+ | set | | guidance + all **reads** |
138
+ | set | `CENCORI_MCP_WRITE=1` | + **writes** (inference, memory, agents, sessions) |
139
+ | set | `CENCORI_MCP_DESTRUCTIVE=1` | + **destructive** (delete/approve/reject); implies write |
130
140
 
131
141
  Restart the MCP server after changing env vars.
132
142
 
package/dist/index.js CHANGED
@@ -200,6 +200,16 @@ var DocsClient = class {
200
200
  }
201
201
  return response.json();
202
202
  }
203
+ /** Fetch the raw `llm.txt` integration contract (public, no auth). */
204
+ async getIntegrationGuide() {
205
+ const url = new URL("/llm.txt", this.baseUrl);
206
+ const response = await fetch(url, { signal: fetchSignal() });
207
+ if (!response.ok) {
208
+ const message = await readHttpErrorMessage(response);
209
+ throw new Error(`Integration guide fetch failed (${response.status}): ${message}`);
210
+ }
211
+ return response.text();
212
+ }
203
213
  };
204
214
 
205
215
  // src/tools/docs.ts
@@ -218,6 +228,12 @@ var WRITE_ANNOTATIONS = {
218
228
  destructiveHint: false,
219
229
  openWorldHint: false
220
230
  };
231
+ var DESTRUCTIVE_ANNOTATIONS = {
232
+ title: "Destructive",
233
+ readOnlyHint: false,
234
+ destructiveHint: true,
235
+ openWorldHint: false
236
+ };
221
237
  function jsonResult(data) {
222
238
  return {
223
239
  content: [
@@ -282,6 +298,28 @@ function registerDocsTools(server, docs, docsBaseUrl) {
282
298
  return jsonResult(navigation);
283
299
  }
284
300
  );
301
+ server.registerTool(
302
+ "get_integration_guide",
303
+ {
304
+ title: "Get the Cencori integration guide (llm.txt)",
305
+ description: 'Fetch the full Cencori "Integration Contract for Code Agents" (llm.txt) \u2014 the authoritative, always-current guide for setting up Cencori in a codebase: package names, import paths, env vars, base URLs, SDK usage, request/response shapes, and step-by-step setup. Call this FIRST when asked to add, integrate, or set up Cencori in a project.',
306
+ inputSchema: {},
307
+ annotations: READ_ONLY_ANNOTATIONS
308
+ },
309
+ async () => {
310
+ const guide = await docs.getIntegrationGuide();
311
+ return {
312
+ content: [
313
+ {
314
+ type: "text",
315
+ text: `Source: ${docsBaseUrl}/llm.txt
316
+
317
+ ${guide}`
318
+ }
319
+ ]
320
+ };
321
+ }
322
+ );
285
323
  }
286
324
 
287
325
  // src/tools/gateway.ts
@@ -339,7 +377,13 @@ function registerGatewayTools(server, client) {
339
377
 
340
378
  // src/tools/agents.ts
341
379
  var import_zod3 = require("zod");
342
- function registerAgentsTools(server, client) {
380
+ var agentConfigShape = import_zod3.z.object({
381
+ model: import_zod3.z.string().optional(),
382
+ system_prompt: import_zod3.z.string().optional(),
383
+ tools: import_zod3.z.array(import_zod3.z.string()).optional(),
384
+ temperature: import_zod3.z.number().min(0).max(2).optional()
385
+ }).optional().describe("Agent runtime config: model, system_prompt, tools, temperature.");
386
+ function registerAgentsTools(server, client, caps) {
343
387
  server.registerTool(
344
388
  "list_agents",
345
389
  {
@@ -378,45 +422,110 @@ function registerAgentsTools(server, client) {
378
422
  },
379
423
  async () => jsonResult(await client.get("/v1/agent/actions/poll"))
380
424
  );
425
+ if (caps.write) {
426
+ server.registerTool(
427
+ "create_agent",
428
+ {
429
+ title: "Create an agent",
430
+ description: "Create a new agent in a project.",
431
+ inputSchema: {
432
+ project_id: import_zod3.z.string().min(1).describe("The project id to create the agent in."),
433
+ name: import_zod3.z.string().min(1).describe("Agent name."),
434
+ description: import_zod3.z.string().optional(),
435
+ config: agentConfigShape
436
+ },
437
+ annotations: WRITE_ANNOTATIONS
438
+ },
439
+ async ({ project_id, name, description, config }) => jsonResult(await client.post("/v1/agents", { project_id, name, description, config }))
440
+ );
441
+ server.registerTool(
442
+ "update_agent",
443
+ {
444
+ title: "Update an agent",
445
+ description: "Update an agent\u2019s name, description, status, shadow mode, or config.",
446
+ inputSchema: {
447
+ agent_id: import_zod3.z.string().min(1).describe("The agent id to update."),
448
+ name: import_zod3.z.string().optional(),
449
+ description: import_zod3.z.string().optional(),
450
+ is_active: import_zod3.z.boolean().optional(),
451
+ shadow_mode: import_zod3.z.boolean().optional(),
452
+ config: agentConfigShape
453
+ },
454
+ annotations: WRITE_ANNOTATIONS
455
+ },
456
+ async ({ agent_id, name, description, is_active, shadow_mode, config }) => jsonResult(
457
+ await client.patch(`/v1/agents/${agent_id}`, {
458
+ name,
459
+ description,
460
+ is_active,
461
+ shadow_mode,
462
+ config
463
+ })
464
+ )
465
+ );
466
+ }
467
+ if (caps.destructive) {
468
+ server.registerTool(
469
+ "delete_agent",
470
+ {
471
+ title: "Delete an agent",
472
+ description: "Permanently delete an agent by id. This cannot be undone.",
473
+ inputSchema: { agent_id: import_zod3.z.string().min(1).describe("The agent id to delete.") },
474
+ annotations: DESTRUCTIVE_ANNOTATIONS
475
+ },
476
+ async ({ agent_id }) => jsonResult(await client.del(`/v1/agents/${agent_id}`))
477
+ );
478
+ }
381
479
  }
382
480
 
383
481
  // src/tools/memory.ts
384
482
  var import_zod4 = require("zod");
385
483
  var scopeShape = {
386
484
  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.")
485
+ scope: import_zod4.z.enum(["user", "session"]).optional().describe('Memory scope. Defaults to "user".'),
486
+ user_id: import_zod4.z.string().optional().describe("End-user id. REQUIRED for user scope (the default)."),
487
+ session_id: import_zod4.z.string().optional().describe("Session id. REQUIRED for session scope.")
390
488
  };
391
- function registerMemoryTools(server, client) {
489
+ function missingScopeKey({ scope, user_id, session_id }) {
490
+ if ((scope ?? "user") === "session") {
491
+ return session_id || user_id ? null : "session_id (or user_id) is required for session scope.";
492
+ }
493
+ return user_id ? null : 'user_id is required for user scope (the default). Pass user_id, or use scope="session" with session_id.';
494
+ }
495
+ var scopeErr = (msg) => jsonResult({ error: "missing_scope", message: msg });
496
+ function registerMemoryTools(server, client, caps) {
392
497
  server.registerTool(
393
498
  "list_memories",
394
499
  {
395
500
  title: "List memories",
396
- description: "List stored memories for the project, optionally scoped by namespace/user/session.",
501
+ description: "List stored memories for a user or session. Requires user_id (or session_id for session scope).",
397
502
  inputSchema: {
398
503
  ...scopeShape,
399
- limit: import_zod4.z.number().int().positive().max(200).optional(),
504
+ limit: import_zod4.z.number().int().positive().max(100).optional(),
400
505
  cursor: import_zod4.z.string().optional().describe("Pagination cursor from a previous response.")
401
506
  },
402
507
  annotations: READ_ONLY_ANNOTATIONS
403
508
  },
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
- )
509
+ async ({ namespace, scope, user_id, session_id, limit, cursor }) => {
510
+ const err = missingScopeKey({ scope, user_id, session_id });
511
+ if (err) return scopeErr(err);
512
+ return jsonResult(
513
+ await client.get("/v1/memory/list", {
514
+ namespace,
515
+ scope,
516
+ userId: user_id,
517
+ sessionId: session_id,
518
+ limit: limit?.toString(),
519
+ cursor
520
+ })
521
+ );
522
+ }
414
523
  );
415
524
  server.registerTool(
416
525
  "search_memory",
417
526
  {
418
527
  title: "Search memory (semantic)",
419
- description: "Semantically search stored memories by query.",
528
+ description: "Semantically search a user\u2019s or session\u2019s memories. Requires user_id (or session_id for session scope).",
420
529
  inputSchema: {
421
530
  query: import_zod4.z.string().min(1).describe("Natural-language search query."),
422
531
  ...scopeShape,
@@ -425,17 +534,21 @@ function registerMemoryTools(server, client) {
425
534
  },
426
535
  annotations: READ_ONLY_ANNOTATIONS
427
536
  },
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
- )
537
+ async ({ query, namespace, scope, user_id, session_id, top_k, threshold }) => {
538
+ const err = missingScopeKey({ scope, user_id, session_id });
539
+ if (err) return scopeErr(err);
540
+ return jsonResult(
541
+ await client.post("/v1/memory/search", {
542
+ query,
543
+ namespace,
544
+ scope,
545
+ userId: user_id,
546
+ sessionId: session_id,
547
+ topK: top_k,
548
+ threshold
549
+ })
550
+ );
551
+ }
439
552
  );
440
553
  server.registerTool(
441
554
  "get_memory",
@@ -451,24 +564,23 @@ function registerMemoryTools(server, client) {
451
564
  "list_memory_entities",
452
565
  {
453
566
  title: "List memory entities",
454
- description: "List entities resolved from the memory graph.",
567
+ description: "List entities resolved from a user\u2019s or session\u2019s memory graph. Requires user_id (or session_id).",
455
568
  inputSchema: scopeShape,
456
569
  annotations: READ_ONLY_ANNOTATIONS
457
570
  },
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
- )
571
+ async ({ namespace, scope, user_id, session_id }) => {
572
+ const err = missingScopeKey({ scope, user_id, session_id });
573
+ if (err) return scopeErr(err);
574
+ return jsonResult(
575
+ await client.get("/v1/memory/entities", { namespace, scope, userId: user_id, sessionId: session_id })
576
+ );
577
+ }
466
578
  );
467
579
  server.registerTool(
468
580
  "get_memory_graph",
469
581
  {
470
582
  title: "Get memory entity graph",
471
- description: "Traverse the memory entity graph from an optional starting entity.",
583
+ description: "Traverse a user\u2019s or session\u2019s memory entity graph. Requires user_id (or session_id).",
472
584
  inputSchema: {
473
585
  ...scopeShape,
474
586
  entity: import_zod4.z.string().optional().describe("Entity to start traversal from."),
@@ -476,39 +588,137 @@ function registerMemoryTools(server, client) {
476
588
  },
477
589
  annotations: READ_ONLY_ANNOTATIONS
478
590
  },
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
- )
591
+ async ({ namespace, scope, user_id, session_id, entity, hops }) => {
592
+ const err = missingScopeKey({ scope, user_id, session_id });
593
+ if (err) return scopeErr(err);
594
+ return jsonResult(
595
+ await client.get("/v1/memory/graph", {
596
+ namespace,
597
+ scope,
598
+ userId: user_id,
599
+ sessionId: session_id,
600
+ entity,
601
+ hops: hops?.toString()
602
+ })
603
+ );
604
+ }
489
605
  );
490
606
  server.registerTool(
491
607
  "get_forget_suggestions",
492
608
  {
493
609
  title: "Get forget suggestions",
494
- description: "List memories the system suggests forgetting (stale/superseded).",
610
+ description: "List memories the system suggests forgetting for a user or session. Requires user_id (or session_id).",
495
611
  inputSchema: scopeShape,
496
612
  annotations: READ_ONLY_ANNOTATIONS
497
613
  },
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
- )
614
+ async ({ namespace, scope, user_id, session_id }) => {
615
+ const err = missingScopeKey({ scope, user_id, session_id });
616
+ if (err) return scopeErr(err);
617
+ return jsonResult(
618
+ await client.get("/v1/memory/forget-suggestions", {
619
+ namespace,
620
+ scope,
621
+ userId: user_id,
622
+ sessionId: session_id
623
+ })
624
+ );
625
+ }
506
626
  );
627
+ if (caps.write) {
628
+ server.registerTool(
629
+ "remember_memory",
630
+ {
631
+ title: "Remember a conversation turn",
632
+ description: "Store a user/assistant exchange as memory. Requires user_id (or session_id for session scope).",
633
+ inputSchema: {
634
+ user: import_zod4.z.string().min(1).describe("The user message."),
635
+ assistant: import_zod4.z.string().min(1).describe("The assistant response."),
636
+ ...scopeShape
637
+ },
638
+ annotations: WRITE_ANNOTATIONS
639
+ },
640
+ async ({ user, assistant, namespace, scope, user_id, session_id }) => {
641
+ const err = missingScopeKey({ scope, user_id, session_id });
642
+ if (err) return scopeErr(err);
643
+ return jsonResult(
644
+ await client.post("/v1/memory/remember", {
645
+ user,
646
+ assistant,
647
+ namespace,
648
+ scope,
649
+ userId: user_id,
650
+ sessionId: session_id
651
+ })
652
+ );
653
+ }
654
+ );
655
+ server.registerTool(
656
+ "write_memory",
657
+ {
658
+ title: "Write a memory",
659
+ description: "Store a single memory (a fact/note). Requires user_id (or session_id for session scope).",
660
+ inputSchema: {
661
+ content: import_zod4.z.string().min(1).describe("The memory content to store."),
662
+ importance: import_zod4.z.number().min(0).max(1).optional().describe("Importance weight 0\u20131."),
663
+ ...scopeShape
664
+ },
665
+ annotations: WRITE_ANNOTATIONS
666
+ },
667
+ async ({ content, importance, namespace, scope, user_id, session_id }) => {
668
+ const err = missingScopeKey({ scope, user_id, session_id });
669
+ if (err) return scopeErr(err);
670
+ return jsonResult(
671
+ await client.post("/v1/memory/write", {
672
+ content,
673
+ importance,
674
+ namespace,
675
+ scope,
676
+ userId: user_id,
677
+ sessionId: session_id
678
+ })
679
+ );
680
+ }
681
+ );
682
+ server.registerTool(
683
+ "create_namespace",
684
+ {
685
+ title: "Create a memory namespace",
686
+ description: "Create a new memory namespace.",
687
+ inputSchema: {
688
+ name: import_zod4.z.string().min(1).describe("Namespace name."),
689
+ description: import_zod4.z.string().optional(),
690
+ embedding_model: import_zod4.z.string().optional().describe("Embedding model for this namespace."),
691
+ dimensions: import_zod4.z.number().int().positive().optional()
692
+ },
693
+ annotations: WRITE_ANNOTATIONS
694
+ },
695
+ async ({ name, description, embedding_model, dimensions }) => jsonResult(
696
+ await client.post("/memory/namespaces", {
697
+ name,
698
+ description,
699
+ embeddingModel: embedding_model,
700
+ dimensions
701
+ })
702
+ )
703
+ );
704
+ }
705
+ if (caps.destructive) {
706
+ server.registerTool(
707
+ "delete_memory",
708
+ {
709
+ title: "Delete a memory",
710
+ description: "Permanently delete a memory by id. This cannot be undone.",
711
+ inputSchema: { memory_id: import_zod4.z.string().min(1).describe("The memory id to delete.") },
712
+ annotations: DESTRUCTIVE_ANNOTATIONS
713
+ },
714
+ async ({ memory_id }) => jsonResult(await client.del(`/v1/memory/${memory_id}`))
715
+ );
716
+ }
507
717
  }
508
718
 
509
719
  // src/tools/sessions.ts
510
720
  var import_zod5 = require("zod");
511
- function registerSessionsTools(server, client) {
721
+ function registerSessionsTools(server, client, caps) {
512
722
  server.registerTool(
513
723
  "list_sessions",
514
724
  {
@@ -551,11 +761,87 @@ function registerSessionsTools(server, client) {
551
761
  },
552
762
  async ({ session_id }) => jsonResult(await client.get(`/v1/sessions/${session_id}/events`))
553
763
  );
764
+ if (caps.write) {
765
+ server.registerTool(
766
+ "create_session",
767
+ {
768
+ title: "Create an agent session",
769
+ description: "Start a new session for an agent.",
770
+ inputSchema: {
771
+ agent_id: import_zod5.z.string().min(1).describe("The agent id to start a session for."),
772
+ metadata: import_zod5.z.record(import_zod5.z.string(), import_zod5.z.unknown()).optional().describe("Optional session metadata.")
773
+ },
774
+ annotations: WRITE_ANNOTATIONS
775
+ },
776
+ async ({ agent_id, metadata }) => jsonResult(await client.post("/v1/sessions", { agent_id, metadata }))
777
+ );
778
+ server.registerTool(
779
+ "add_session_turn",
780
+ {
781
+ title: "Add a turn to a session",
782
+ description: "Run a turn in an agent session. Incurs usage/cost.",
783
+ inputSchema: {
784
+ session_id: import_zod5.z.string().min(1).describe("The session id."),
785
+ model: import_zod5.z.string().min(1).describe("Model id for the turn."),
786
+ input: import_zod5.z.string().min(1).describe("The user input for this turn."),
787
+ instructions: import_zod5.z.string().optional().describe("Optional system instructions."),
788
+ temperature: import_zod5.z.number().min(0).max(2).optional()
789
+ },
790
+ annotations: WRITE_ANNOTATIONS
791
+ },
792
+ async ({ session_id, model, input, instructions, temperature }) => jsonResult(
793
+ await client.post(`/v1/sessions/${session_id}/turns`, {
794
+ model,
795
+ input,
796
+ instructions,
797
+ temperature
798
+ })
799
+ )
800
+ );
801
+ }
802
+ if (caps.destructive) {
803
+ server.registerTool(
804
+ "delete_session",
805
+ {
806
+ title: "Delete an agent session",
807
+ description: "Permanently delete an agent session by id. This cannot be undone.",
808
+ inputSchema: { session_id: import_zod5.z.string().min(1).describe("The session id to delete.") },
809
+ annotations: DESTRUCTIVE_ANNOTATIONS
810
+ },
811
+ async ({ session_id }) => jsonResult(await client.del(`/v1/sessions/${session_id}`))
812
+ );
813
+ server.registerTool(
814
+ "approve_session",
815
+ {
816
+ title: "Approve a pending session action",
817
+ description: "Approve a pending action in an agent session (human-in-the-loop).",
818
+ inputSchema: {
819
+ session_id: import_zod5.z.string().min(1).describe("The session id."),
820
+ action_id: import_zod5.z.string().optional().describe("Specific pending action id, if any.")
821
+ },
822
+ annotations: DESTRUCTIVE_ANNOTATIONS
823
+ },
824
+ async ({ session_id, action_id }) => jsonResult(await client.post(`/v1/sessions/${session_id}/approve`, { action_id }))
825
+ );
826
+ server.registerTool(
827
+ "reject_session",
828
+ {
829
+ title: "Reject a pending session action",
830
+ description: "Reject a pending action in an agent session (human-in-the-loop).",
831
+ inputSchema: {
832
+ session_id: import_zod5.z.string().min(1).describe("The session id."),
833
+ action_id: import_zod5.z.string().optional().describe("Specific pending action id, if any.")
834
+ },
835
+ annotations: DESTRUCTIVE_ANNOTATIONS
836
+ },
837
+ async ({ session_id, action_id }) => jsonResult(await client.post(`/v1/sessions/${session_id}/reject`, { action_id }))
838
+ );
839
+ }
554
840
  }
555
841
 
556
842
  // src/tools/governance.ts
557
843
  var import_zod6 = require("zod");
558
- function registerGovernanceTools(server, client) {
844
+ function registerGovernanceTools(server, client, caps) {
559
845
  server.registerTool(
560
846
  "list_policies",
561
847
  {
@@ -618,6 +904,34 @@ function registerGovernanceTools(server, client) {
618
904
  },
619
905
  async () => jsonResult(await client.get("/v1/governance/templates"))
620
906
  );
907
+ if (caps.write) {
908
+ server.registerTool(
909
+ "create_policy",
910
+ {
911
+ title: "Draft a governance policy",
912
+ description: "Create a governance policy as a DRAFT. It is not enforced until a human activates it (see how_to_activate_policy).",
913
+ inputSchema: {
914
+ name: import_zod6.z.string().min(1).describe("Policy name (unique per org)."),
915
+ spec: import_zod6.z.record(import_zod6.z.string(), import_zod6.z.unknown()).describe("Policy spec object: match + rules + defaults + controls.")
916
+ },
917
+ annotations: WRITE_ANNOTATIONS
918
+ },
919
+ async ({ name, spec }) => jsonResult(await client.post("/v1/governance/policies", { name, spec }))
920
+ );
921
+ server.registerTool(
922
+ "install_template",
923
+ {
924
+ title: "Install a governance policy template",
925
+ description: "Install a policy template as a new DRAFT policy. Activation remains a manual human step (see how_to_activate_policy).",
926
+ inputSchema: {
927
+ template_id: import_zod6.z.string().min(1).describe("The template id to install."),
928
+ name: import_zod6.z.string().min(1).describe("Name for the new draft policy.")
929
+ },
930
+ annotations: WRITE_ANNOTATIONS
931
+ },
932
+ async ({ template_id, name }) => jsonResult(await client.post(`/v1/governance/templates/${template_id}/install`, { name }))
933
+ );
934
+ }
621
935
  }
622
936
 
623
937
  // src/tools/multimodal.ts
@@ -900,7 +1214,7 @@ function registerGuidanceTools(server, baseUrl) {
900
1214
 
901
1215
  // src/server.ts
902
1216
  var SERVER_NAME = "cencori";
903
- var SERVER_VERSION = "0.2.0";
1217
+ var SERVER_VERSION = "0.5.0";
904
1218
  function createServer(config) {
905
1219
  const server = new import_mcp.McpServer(
906
1220
  {
@@ -925,10 +1239,10 @@ function createServer(config) {
925
1239
  if (config.apiKey) {
926
1240
  const client = new PlatformClient(config.baseUrl, config.apiKey);
927
1241
  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);
1242
+ if (features.agents) registerAgentsTools(server, client, capabilities);
1243
+ if (features.memory) registerMemoryTools(server, client, capabilities);
1244
+ if (features.sessions) registerSessionsTools(server, client, capabilities);
1245
+ if (features.governance) registerGovernanceTools(server, client, capabilities);
932
1246
  if (features.multimodal && capabilities.write) {
933
1247
  registerMultimodalTools(server, client);
934
1248
  }