coaiajs 0.1.1 → 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.
Files changed (175) hide show
  1. package/.claude/settings.local.json +16 -0
  2. package/README.md +9 -3
  3. package/articles/academic/NOTES.md +1 -0
  4. package/articles/academic/README.md +1 -0
  5. package/articles/academic/creative-orientation-vs-problem-solving.md +177 -0
  6. package/articles/academic/jsonl-knowledge-graphs-agent-memory.md +142 -0
  7. package/articles/academic/langfuse-observability-llm-pipelines.md +144 -0
  8. package/articles/academic/medicine-wheel-software-architecture.md +163 -0
  9. package/articles/academic/mmot-autonomous-agents.md +156 -0
  10. package/articles/academic/model-context-protocol-interagent.md +161 -0
  11. package/articles/academic/pde-prompt-decomposition.md +186 -0
  12. package/articles/academic/structural-tension-in-ai-agents.md +134 -0
  13. package/articles/reviews/mcp-protocol-design-review.md +170 -0
  14. package/articles/reviews/observability-ai-systems-review.md +176 -0
  15. package/articles/reviews/prompt-engineering-decomposition-review.md +184 -0
  16. package/articles/surveys/agent-orchestration-survey.md +186 -0
  17. package/articles/surveys/knowledge-graph-storage-survey.md +204 -0
  18. package/articles/surveys/structural-tension-methodology-survey.md +154 -0
  19. package/articles/technical/aws-sdk-v3-polly.md +270 -0
  20. package/articles/technical/commander-cli-framework.md +262 -0
  21. package/articles/technical/dotenv-config-patterns.md +360 -0
  22. package/articles/technical/ioredis-vs-redis.md +142 -0
  23. package/articles/technical/langfuse-js-sdk-vs-rest.md +191 -0
  24. package/articles/technical/mcp-sdk-typescript.md +291 -0
  25. package/articles/technical/octokit-github-api.md +293 -0
  26. package/articles/technical/openai-sdk-modern.md +231 -0
  27. package/articles/technical/yaml-parsing-node.md +266 -0
  28. package/articles/technical/zod-runtime-validation.md +212 -0
  29. package/dist/mcp/prompts.js +1 -1
  30. package/dist/mcp/server.js +8 -5
  31. package/dist/mcp/tools/coaiapy-tools.js +1 -0
  32. package/dist/src/cli.js +43 -7
  33. package/dist/src/config.d.ts +2 -0
  34. package/dist/src/config.js +44 -14
  35. package/dist/src/langfuse/index.d.ts +1 -1
  36. package/dist/src/langfuse/index.js +1 -1
  37. package/dist/src/langfuse/traces.d.ts +1 -0
  38. package/dist/src/langfuse/traces.js +24 -0
  39. package/dist/src/redis.d.ts +1 -1
  40. package/dist/src/redis.js +22 -10
  41. package/dist/src/types.d.ts +1 -0
  42. package/mcp/config.ts +225 -0
  43. package/mcp/prompts.ts +131 -0
  44. package/mcp/resources.ts +84 -0
  45. package/mcp/server.ts +518 -0
  46. package/mcp/tools/coaiapy-tools.ts +366 -0
  47. package/mcp/tools/index.ts +4 -0
  48. package/package.json +1 -66
  49. package/rispecs/00-coaiajs-platform.spec.md +6 -5
  50. package/rispecs/09-mcp-server.spec.md +16 -18
  51. package/src/audio.ts +76 -0
  52. package/src/cli-helpers.ts +86 -0
  53. package/src/cli.ts +1260 -0
  54. package/src/config.ts +207 -0
  55. package/src/environment.ts +171 -0
  56. package/src/github.ts +143 -0
  57. package/src/index.ts +82 -0
  58. package/src/langfuse/client.ts +105 -0
  59. package/src/langfuse/comments.ts +52 -0
  60. package/src/langfuse/datasets.ts +178 -0
  61. package/src/langfuse/index.ts +33 -0
  62. package/src/langfuse/media.ts +193 -0
  63. package/src/langfuse/observations.ts +131 -0
  64. package/src/langfuse/prompts.ts +157 -0
  65. package/src/langfuse/scores.ts +456 -0
  66. package/src/langfuse/traces.ts +302 -0
  67. package/src/llm.ts +106 -0
  68. package/src/narrative/graph-manager.ts +1358 -0
  69. package/src/narrative/index.ts +188 -0
  70. package/src/narrative/markdown-export.ts +535 -0
  71. package/src/narrative/tool-definitions.ts +635 -0
  72. package/src/narrative/tool-handlers.ts +528 -0
  73. package/src/narrative/types.ts +9 -0
  74. package/src/narrative/validation.ts +179 -0
  75. package/src/pde/index.ts +34 -0
  76. package/src/pde/mcp-handlers.ts +359 -0
  77. package/src/pde/mcp-tools.ts +201 -0
  78. package/src/pde/session-manager.ts +248 -0
  79. package/src/pde/stc-mapper.ts +298 -0
  80. package/src/pipeline/index.ts +7 -0
  81. package/src/pipeline/template-engine.ts +398 -0
  82. package/src/planning/index.ts +32 -0
  83. package/src/planning/mcp-handlers.ts +369 -0
  84. package/src/planning/mcp-tools.ts +155 -0
  85. package/src/planning/plan-parser.ts +587 -0
  86. package/src/redis.ts +111 -0
  87. package/src/types.ts +281 -0
  88. package/test/config.test.mjs +93 -0
  89. package/tsconfig.json +26 -0
  90. package/dist/mcp/config.d.ts.map +0 -1
  91. package/dist/mcp/config.js.map +0 -1
  92. package/dist/mcp/prompts.d.ts.map +0 -1
  93. package/dist/mcp/prompts.js.map +0 -1
  94. package/dist/mcp/resources.d.ts.map +0 -1
  95. package/dist/mcp/resources.js.map +0 -1
  96. package/dist/mcp/server.d.ts.map +0 -1
  97. package/dist/mcp/server.js.map +0 -1
  98. package/dist/mcp/tools/coaiapy-tools.d.ts.map +0 -1
  99. package/dist/mcp/tools/coaiapy-tools.js.map +0 -1
  100. package/dist/mcp/tools/index.d.ts.map +0 -1
  101. package/dist/mcp/tools/index.js.map +0 -1
  102. package/dist/src/audio.d.ts.map +0 -1
  103. package/dist/src/audio.js.map +0 -1
  104. package/dist/src/cli-helpers.d.ts.map +0 -1
  105. package/dist/src/cli-helpers.js.map +0 -1
  106. package/dist/src/cli.d.ts.map +0 -1
  107. package/dist/src/cli.js.map +0 -1
  108. package/dist/src/config.d.ts.map +0 -1
  109. package/dist/src/config.js.map +0 -1
  110. package/dist/src/environment.d.ts.map +0 -1
  111. package/dist/src/environment.js.map +0 -1
  112. package/dist/src/github.d.ts.map +0 -1
  113. package/dist/src/github.js.map +0 -1
  114. package/dist/src/index.d.ts.map +0 -1
  115. package/dist/src/index.js.map +0 -1
  116. package/dist/src/langfuse/client.d.ts.map +0 -1
  117. package/dist/src/langfuse/client.js.map +0 -1
  118. package/dist/src/langfuse/comments.d.ts.map +0 -1
  119. package/dist/src/langfuse/comments.js.map +0 -1
  120. package/dist/src/langfuse/datasets.d.ts.map +0 -1
  121. package/dist/src/langfuse/datasets.js.map +0 -1
  122. package/dist/src/langfuse/index.d.ts.map +0 -1
  123. package/dist/src/langfuse/index.js.map +0 -1
  124. package/dist/src/langfuse/media.d.ts.map +0 -1
  125. package/dist/src/langfuse/media.js.map +0 -1
  126. package/dist/src/langfuse/observations.d.ts.map +0 -1
  127. package/dist/src/langfuse/observations.js.map +0 -1
  128. package/dist/src/langfuse/prompts.d.ts.map +0 -1
  129. package/dist/src/langfuse/prompts.js.map +0 -1
  130. package/dist/src/langfuse/scores.d.ts.map +0 -1
  131. package/dist/src/langfuse/scores.js.map +0 -1
  132. package/dist/src/langfuse/traces.d.ts.map +0 -1
  133. package/dist/src/langfuse/traces.js.map +0 -1
  134. package/dist/src/llm.d.ts.map +0 -1
  135. package/dist/src/llm.js.map +0 -1
  136. package/dist/src/narrative/graph-manager.d.ts.map +0 -1
  137. package/dist/src/narrative/graph-manager.js.map +0 -1
  138. package/dist/src/narrative/index.d.ts.map +0 -1
  139. package/dist/src/narrative/index.js.map +0 -1
  140. package/dist/src/narrative/markdown-export.d.ts.map +0 -1
  141. package/dist/src/narrative/markdown-export.js.map +0 -1
  142. package/dist/src/narrative/tool-definitions.d.ts.map +0 -1
  143. package/dist/src/narrative/tool-definitions.js.map +0 -1
  144. package/dist/src/narrative/tool-handlers.d.ts.map +0 -1
  145. package/dist/src/narrative/tool-handlers.js.map +0 -1
  146. package/dist/src/narrative/types.d.ts.map +0 -1
  147. package/dist/src/narrative/types.js.map +0 -1
  148. package/dist/src/narrative/validation.d.ts.map +0 -1
  149. package/dist/src/narrative/validation.js.map +0 -1
  150. package/dist/src/pde/index.d.ts.map +0 -1
  151. package/dist/src/pde/index.js.map +0 -1
  152. package/dist/src/pde/mcp-handlers.d.ts.map +0 -1
  153. package/dist/src/pde/mcp-handlers.js.map +0 -1
  154. package/dist/src/pde/mcp-tools.d.ts.map +0 -1
  155. package/dist/src/pde/mcp-tools.js.map +0 -1
  156. package/dist/src/pde/session-manager.d.ts.map +0 -1
  157. package/dist/src/pde/session-manager.js.map +0 -1
  158. package/dist/src/pde/stc-mapper.d.ts.map +0 -1
  159. package/dist/src/pde/stc-mapper.js.map +0 -1
  160. package/dist/src/pipeline/index.d.ts.map +0 -1
  161. package/dist/src/pipeline/index.js.map +0 -1
  162. package/dist/src/pipeline/template-engine.d.ts.map +0 -1
  163. package/dist/src/pipeline/template-engine.js.map +0 -1
  164. package/dist/src/planning/index.d.ts.map +0 -1
  165. package/dist/src/planning/index.js.map +0 -1
  166. package/dist/src/planning/mcp-handlers.d.ts.map +0 -1
  167. package/dist/src/planning/mcp-handlers.js.map +0 -1
  168. package/dist/src/planning/mcp-tools.d.ts.map +0 -1
  169. package/dist/src/planning/mcp-tools.js.map +0 -1
  170. package/dist/src/planning/plan-parser.d.ts.map +0 -1
  171. package/dist/src/planning/plan-parser.js.map +0 -1
  172. package/dist/src/redis.d.ts.map +0 -1
  173. package/dist/src/redis.js.map +0 -1
  174. package/dist/src/types.d.ts.map +0 -1
  175. package/dist/src/types.js.map +0 -1
@@ -1,10 +1,22 @@
1
1
  // coaiajs/src/config.ts — Config management
2
2
  // Priority: env vars > .env > coaia.json > defaults
3
3
  import { readFileSync, existsSync } from 'node:fs';
4
- import { resolve, join } from 'node:path';
4
+ import { resolve, join, isAbsolute } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
6
  import dotenv from 'dotenv';
7
- const CONFIG_FILENAMES = ['coaia.json', '.coaia/config.json'];
7
+ const CONFIG_FILENAMES = [
8
+ 'coaia.json',
9
+ '.coaia/config.json',
10
+ '.config/jgwill/coaia.json',
11
+ 'Documents/coaia.json',
12
+ ];
13
+ function expandPath(filePath) {
14
+ if (filePath === '~')
15
+ return homedir();
16
+ if (filePath.startsWith('~/'))
17
+ return join(homedir(), filePath.slice(2));
18
+ return isAbsolute(filePath) ? filePath : resolve(process.cwd(), filePath);
19
+ }
8
20
  /** Search for an existing coaia.json config file. */
9
21
  export function findExistingConfig() {
10
22
  const searchDirs = [process.cwd(), homedir()];
@@ -18,6 +30,17 @@ export function findExistingConfig() {
18
30
  }
19
31
  return null;
20
32
  }
33
+ /** Resolve dotenv files in priority order. Earlier files win over later files. */
34
+ export function findEnvFiles(envPath) {
35
+ const explicitEnvPath = envPath ?? process.env['COAIAJS_ENV_PATH'] ?? process.env['COAIAPY_ENV_PATH'];
36
+ if (explicitEnvPath) {
37
+ return [expandPath(explicitEnvPath)].filter((candidate) => existsSync(candidate));
38
+ }
39
+ return [
40
+ resolve(process.cwd(), '.env'),
41
+ join(homedir(), '.coaia', '.env'),
42
+ ].filter((candidate) => existsSync(candidate));
43
+ }
21
44
  /** Deep-merge two config objects; override wins on conflicts. */
22
45
  export function mergeConfigs(base, override) {
23
46
  const result = { ...base };
@@ -46,18 +69,29 @@ function loadJsonConfig(filePath) {
46
69
  return {};
47
70
  }
48
71
  }
72
+ function parseBool(value) {
73
+ if (!value)
74
+ return undefined;
75
+ return ['1', 'true', 'yes', 'on'].includes(value.toLowerCase());
76
+ }
49
77
  function envOverrides() {
50
78
  const cfg = {};
51
79
  const env = process.env;
52
80
  // Redis
53
- if (env['REDIS_URL'] || env['REDIS_HOST'] || env['UPSTASH_REDIS_REST_URL']) {
81
+ const upstashUrl = env['UPSTASH_REDIS_REST_URL'] ?? env['KV_REST_API_URL'];
82
+ const upstashToken = env['UPSTASH_REDIS_REST_TOKEN'] ?? env['KV_REST_API_TOKEN'];
83
+ const redisUrl = env['KV_URL'] ?? env['REDIS_URL'];
84
+ const redisHost = env['REDIS_HOST'] ?? env['UPSTASH_HOST'];
85
+ const redisPassword = env['REDIS_PASSWORD'] ?? env['UPSTASH_PASSWORD'];
86
+ if (upstashUrl || redisUrl || redisHost || env['REDIS_PORT'] || redisPassword) {
54
87
  cfg.redis = {
55
- url: env['REDIS_URL'],
56
- host: env['REDIS_HOST'],
88
+ url: redisUrl,
89
+ host: redisHost,
57
90
  port: env['REDIS_PORT'] ? parseInt(env['REDIS_PORT'], 10) : undefined,
58
- password: env['REDIS_PASSWORD'],
59
- upstashUrl: env['UPSTASH_REDIS_REST_URL'],
60
- upstashToken: env['UPSTASH_REDIS_REST_TOKEN'],
91
+ password: redisPassword,
92
+ ssl: parseBool(env['REDIS_SSL']),
93
+ upstashUrl,
94
+ upstashToken,
61
95
  };
62
96
  }
63
97
  // Langfuse
@@ -97,12 +131,8 @@ function envOverrides() {
97
131
  */
98
132
  export function readConfig(envPath) {
99
133
  // 1. Load .env (lowest priority file source)
100
- const dotenvPath = envPath ?? resolve(process.cwd(), '.env');
101
- if (existsSync(dotenvPath)) {
102
- dotenv.config({ path: dotenvPath });
103
- }
104
- else {
105
- dotenv.config(); // default search
134
+ for (const dotenvPath of findEnvFiles(envPath)) {
135
+ dotenv.config({ path: dotenvPath, override: false });
106
136
  }
107
137
  // 2. Load coaia.json
108
138
  const configPath = findExistingConfig();
@@ -1,6 +1,6 @@
1
1
  export { LangfuseClient, LangfuseApiError, getClient, resetClient, nowISO } from './client.js';
2
2
  export type { IngestionEvent, LangfuseClientConfig } from './client.js';
3
- export { addTrace, patchTraceOutput, listTraces, getTrace, formatTracesTable, formatTraceTree } from './traces.js';
3
+ export { addTrace, patchTraceOutput, listTraces, getTrace, formatTracesTable, formatTracesMarkdown, formatTraceTree } from './traces.js';
4
4
  export type { TraceFilters } from './traces.js';
5
5
  export { addObservation, getObservation, formatObservationDisplay } from './observations.js';
6
6
  export { listPrompts, getPrompt, createPrompt, formatPromptsTable, formatPromptDisplay } from './prompts.js';
@@ -1,6 +1,6 @@
1
1
  // coaiajs/src/langfuse/index.ts — Barrel export
2
2
  export { LangfuseClient, LangfuseApiError, getClient, resetClient, nowISO } from './client.js';
3
- export { addTrace, patchTraceOutput, listTraces, getTrace, formatTracesTable, formatTraceTree } from './traces.js';
3
+ export { addTrace, patchTraceOutput, listTraces, getTrace, formatTracesTable, formatTracesMarkdown, formatTraceTree } from './traces.js';
4
4
  export { addObservation, getObservation, formatObservationDisplay } from './observations.js';
5
5
  export { listPrompts, getPrompt, createPrompt, formatPromptsTable, formatPromptDisplay } from './prompts.js';
6
6
  export { listDatasets, getDataset, createDataset, listDatasetItems, createDatasetItem, formatDatasetsTable, formatDatasetForFinetuning, } from './datasets.js';
@@ -26,4 +26,5 @@ export declare function listTraces(filters: TraceFilters): Promise<string>;
26
26
  export declare function getTrace(traceId: string): Promise<string>;
27
27
  export declare function formatTracesTable(json: unknown): string;
28
28
  export declare function formatTraceTree(json: unknown): string;
29
+ export declare function formatTracesMarkdown(json: unknown): string;
29
30
  //# sourceMappingURL=traces.d.ts.map
@@ -211,6 +211,30 @@ export function formatTraceTree(json) {
211
211
  return `Error formatting trace tree: ${e}`;
212
212
  }
213
213
  }
214
+ export function formatTracesMarkdown(json) {
215
+ try {
216
+ const data = typeof json === 'string' ? JSON.parse(json) : json;
217
+ const traces = Array.isArray(data)
218
+ ? data
219
+ : data.data ?? [];
220
+ if (!traces.length)
221
+ return '_No traces found._';
222
+ const header = '| ID | Name | Session | User | Timestamp |';
223
+ const sep = '|----|------|---------|------|-----------|';
224
+ const rows = traces.map((t) => {
225
+ const id = trunc(String(t.id ?? ''), 20);
226
+ const name = trunc(String(t.name ?? 'Unnamed'), 30);
227
+ const session = trunc(String(t.sessionId ?? ''), 24);
228
+ const user = trunc(String(t.userId ?? ''), 16);
229
+ const ts = String(t.timestamp ?? '').slice(0, 19);
230
+ return `| ${id} | ${name} | ${session} | ${user} | ${ts} |`;
231
+ });
232
+ return [header, sep, ...rows, '', `_Total: ${traces.length} trace(s)_`].join('\n');
233
+ }
234
+ catch (e) {
235
+ return `Error formatting traces: ${e}`;
236
+ }
237
+ }
214
238
  // ─── Helpers ────────────────────────────────────────────────────────
215
239
  function trunc(s, len) {
216
240
  return s.length > len ? s.slice(0, len - 3) + '...' : s;
@@ -3,7 +3,7 @@ declare const Redis: typeof IORedis.Redis;
3
3
  type RedisClient = InstanceType<typeof Redis>;
4
4
  /** Get or create the lazy Redis client. */
5
5
  export declare function getClient(): RedisClient;
6
- /** Store a key-value pair with optional TTL (seconds). */
6
+ /** Store a key-value pair with optional TTL in minutes, matching coaiapy. */
7
7
  export declare function tash(key: string, value: string, ttl?: number): Promise<void>;
8
8
  /** Fetch a value by key. Returns null if not found. */
9
9
  export declare function fetch(key: string): Promise<string | null>;
package/dist/src/redis.js CHANGED
@@ -4,19 +4,30 @@ import IORedis from 'ioredis';
4
4
  import { getConfig } from './config.js';
5
5
  const Redis = IORedis.default ?? IORedis;
6
6
  let _client = null;
7
+ function upstashClient(url, token) {
8
+ const parsed = new URL(url);
9
+ const tls = parsed.protocol === 'https:' || parsed.protocol === 'rediss:';
10
+ const password = token ?? decodeURIComponent(parsed.password);
11
+ return new Redis({
12
+ host: parsed.hostname,
13
+ port: parsed.port ? parseInt(parsed.port, 10) : 6379,
14
+ username: parsed.username ? decodeURIComponent(parsed.username) : undefined,
15
+ password: password || undefined,
16
+ lazyConnect: true,
17
+ tls: tls ? {} : undefined,
18
+ });
19
+ }
7
20
  function buildClient(cfg) {
8
21
  const r = cfg.redis;
22
+ // Upstash/Vercel REST env vars take priority, matching coaiapy.
23
+ if (r?.upstashUrl) {
24
+ return upstashClient(r.upstashUrl, r.upstashToken);
25
+ }
9
26
  // Explicit URL takes priority
10
27
  if (r?.url) {
11
- return new Redis(r.url, { lazyConnect: true });
12
- }
13
- // Upstash REST-style URL (ioredis can connect via rediss:// scheme)
14
- if (r?.upstashUrl) {
15
- const url = r.upstashUrl.replace(/^https:\/\//, 'rediss://');
16
- return new Redis(url, {
17
- password: r.upstashToken,
28
+ return new Redis(r.url, {
18
29
  lazyConnect: true,
19
- tls: {},
30
+ tls: r.url.startsWith('rediss://') ? {} : undefined,
20
31
  });
21
32
  }
22
33
  // Host / port fallback
@@ -25,6 +36,7 @@ function buildClient(cfg) {
25
36
  port: r?.port ?? 6379,
26
37
  password: r?.password,
27
38
  lazyConnect: true,
39
+ tls: r?.ssl ? {} : undefined,
28
40
  });
29
41
  }
30
42
  /** Get or create the lazy Redis client. */
@@ -34,11 +46,11 @@ export function getClient() {
34
46
  }
35
47
  return _client;
36
48
  }
37
- /** Store a key-value pair with optional TTL (seconds). */
49
+ /** Store a key-value pair with optional TTL in minutes, matching coaiapy. */
38
50
  export async function tash(key, value, ttl) {
39
51
  const client = getClient();
40
52
  if (ttl && ttl > 0) {
41
- await client.set(key, value, 'EX', ttl);
53
+ await client.set(key, value, 'EX', ttl * 60);
42
54
  }
43
55
  else {
44
56
  await client.set(key, value);
@@ -219,6 +219,7 @@ export interface CoaiaConfig {
219
219
  host?: string;
220
220
  port?: number;
221
221
  password?: string;
222
+ ssl?: boolean;
222
223
  upstashUrl?: string;
223
224
  upstashToken?: string;
224
225
  };
package/mcp/config.ts ADDED
@@ -0,0 +1,225 @@
1
+ // coaiajs/mcp/config.ts — Feature gating
2
+ // Port of coaiapy-mcp/config.py
3
+
4
+ export type FeatureLevel = 'MINIMAL' | 'STANDARD' | 'OBSERVABILITY' | 'FULL';
5
+
6
+ // All available tool names across all modules
7
+ const ALL_TOOLS = new Set([
8
+ // Redis (coaiapy-mcp)
9
+ 'coaia_tash',
10
+ 'coaia_fetch',
11
+ // Langfuse traces (coaiapy-mcp)
12
+ 'coaia_fuse_trace_create',
13
+ 'coaia_fuse_add_observation',
14
+ 'coaia_fuse_trace_patch_output',
15
+ 'coaia_fuse_trace_get',
16
+ 'coaia_fuse_trace_view',
17
+ 'coaia_fuse_observation_get',
18
+ 'coaia_fuse_traces_list',
19
+ 'coaia_fuse_traces_session_view',
20
+ // Langfuse comments (coaiapy-mcp)
21
+ 'coaia_fuse_comments_list',
22
+ 'coaia_fuse_comments_get',
23
+ 'coaia_fuse_comments_create',
24
+ // Langfuse prompts (coaiapy-mcp)
25
+ 'coaia_fuse_prompts_list',
26
+ 'coaia_fuse_prompts_get',
27
+ // Langfuse datasets (coaiapy-mcp)
28
+ 'coaia_fuse_datasets_list',
29
+ 'coaia_fuse_datasets_get',
30
+ // Score configs (coaiapy-mcp)
31
+ 'coaia_fuse_score_configs_list',
32
+ 'coaia_fuse_score_configs_get',
33
+ 'coaia_fuse_score_apply',
34
+ // Media (coaiapy-mcp, FULL only)
35
+ 'coaia_fuse_media_upload',
36
+ 'coaia_fuse_media_get',
37
+ // Narrative (coaia-narrative, 28 tools)
38
+ 'create_structural_tension_chart',
39
+ 'telescope_action_step',
40
+ 'mark_action_complete',
41
+ 'get_chart_progress',
42
+ 'list_active_charts',
43
+ 'get_chart',
44
+ 'get_action_step',
45
+ 'update_action_progress',
46
+ 'update_current_reality',
47
+ 'manage_action_step',
48
+ 'add_action_step',
49
+ 'remove_action_step',
50
+ 'update_desired_outcome',
51
+ 'perform_mmot_evaluation',
52
+ 'init_llm_guidance',
53
+ 'create_narrative_beat',
54
+ 'telescope_narrative_beat',
55
+ 'list_narrative_beats',
56
+ 'add_observations',
57
+ 'create_entities',
58
+ 'create_relations',
59
+ 'search_nodes',
60
+ 'open_nodes',
61
+ 'read_graph',
62
+ 'delete_entities',
63
+ 'delete_relations',
64
+ 'delete_observations',
65
+ 'merge_entities',
66
+ // PDE (coaia-pde, 10 tools)
67
+ 'import_pde_decomposition',
68
+ 'create_stc_from_pde',
69
+ 'list_pde_decompositions',
70
+ 'get_session',
71
+ 'list_sessions',
72
+ 'complete_session',
73
+ 'pde_update_action_progress',
74
+ 'pde_mark_action_complete',
75
+ 'pde_add_action_step',
76
+ 'pde_update_current_reality',
77
+ // Planning (coaia-planning, 6 tools)
78
+ 'parse_plan_structural',
79
+ 'plan_to_stc',
80
+ 'sync_plan_to_chart',
81
+ 'sync_chart_to_plan',
82
+ 'create_plan_trace',
83
+ 'pde_to_plan',
84
+ ]);
85
+
86
+ const MINIMAL_TOOLS = new Set([
87
+ 'coaia_tash',
88
+ 'coaia_fetch',
89
+ 'coaia_fuse_trace_create',
90
+ 'coaia_fuse_add_observation',
91
+ 'coaia_fuse_trace_patch_output',
92
+ 'coaia_fuse_trace_get',
93
+ 'coaia_fuse_trace_view',
94
+ 'coaia_fuse_observation_get',
95
+ 'coaia_fuse_traces_list',
96
+ 'coaia_fuse_traces_session_view',
97
+ 'coaia_fuse_comments_list',
98
+ 'coaia_fuse_comments_get',
99
+ 'coaia_fuse_comments_create',
100
+ 'coaia_fuse_prompts_list',
101
+ 'coaia_fuse_prompts_get',
102
+ 'coaia_fuse_datasets_list',
103
+ 'coaia_fuse_datasets_get',
104
+ 'coaia_fuse_score_configs_list',
105
+ 'coaia_fuse_score_configs_get',
106
+ 'coaia_fuse_score_apply',
107
+ ]);
108
+
109
+ const STANDARD_TOOLS = new Set([
110
+ ...MINIMAL_TOOLS,
111
+ // Narrative STC tools
112
+ 'create_structural_tension_chart',
113
+ 'telescope_action_step',
114
+ 'mark_action_complete',
115
+ 'get_chart_progress',
116
+ 'list_active_charts',
117
+ 'get_chart',
118
+ 'get_action_step',
119
+ 'update_action_progress',
120
+ 'update_current_reality',
121
+ 'manage_action_step',
122
+ 'add_action_step',
123
+ 'remove_action_step',
124
+ 'update_desired_outcome',
125
+ 'perform_mmot_evaluation',
126
+ 'init_llm_guidance',
127
+ 'create_narrative_beat',
128
+ 'telescope_narrative_beat',
129
+ 'list_narrative_beats',
130
+ // KG tools
131
+ 'add_observations',
132
+ 'create_entities',
133
+ 'create_relations',
134
+ 'search_nodes',
135
+ 'open_nodes',
136
+ 'read_graph',
137
+ 'delete_entities',
138
+ 'delete_relations',
139
+ 'delete_observations',
140
+ 'merge_entities',
141
+ // PDE tools
142
+ 'import_pde_decomposition',
143
+ 'create_stc_from_pde',
144
+ 'list_pde_decompositions',
145
+ 'get_session',
146
+ 'list_sessions',
147
+ 'complete_session',
148
+ 'pde_update_action_progress',
149
+ 'pde_mark_action_complete',
150
+ 'pde_add_action_step',
151
+ 'pde_update_current_reality',
152
+ // Planning tools
153
+ 'parse_plan_structural',
154
+ 'plan_to_stc',
155
+ 'sync_plan_to_chart',
156
+ 'sync_chart_to_plan',
157
+ 'create_plan_trace',
158
+ 'pde_to_plan',
159
+ ]);
160
+
161
+ const ALL_RESOURCES = new Set([
162
+ 'coaia://templates/',
163
+ 'coaia://templates/{name}',
164
+ 'coaia://templates/{name}/variables',
165
+ ]);
166
+
167
+ const ALL_PROMPTS = new Set([
168
+ 'mia_miette_duo',
169
+ 'create_observability_pipeline',
170
+ 'analyze_audio_workflow',
171
+ ]);
172
+
173
+ const FEATURE_SETS: Record<FeatureLevel, Set<string>> = {
174
+ MINIMAL: MINIMAL_TOOLS,
175
+ STANDARD: STANDARD_TOOLS,
176
+ OBSERVABILITY: STANDARD_TOOLS,
177
+ FULL: ALL_TOOLS,
178
+ };
179
+
180
+ export class FeatureConfig {
181
+ private level: FeatureLevel;
182
+ private enabledTools: Set<string>;
183
+
184
+ constructor(level?: FeatureLevel) {
185
+ const envLevel = (level ?? process.env['COAIAJS_FEATURES'] ?? 'STANDARD').toUpperCase() as FeatureLevel;
186
+ this.level = envLevel in FEATURE_SETS ? envLevel : 'STANDARD';
187
+ this.enabledTools = FEATURE_SETS[this.level];
188
+ }
189
+
190
+ isToolEnabled(toolName: string): boolean {
191
+ return this.enabledTools.has(toolName);
192
+ }
193
+
194
+ isResourceEnabled(uri: string): boolean {
195
+ if (uri === 'coaia://templates/') return ALL_RESOURCES.has(uri);
196
+ if (uri.startsWith('coaia://templates/') && uri.endsWith('/variables')) {
197
+ return ALL_RESOURCES.has('coaia://templates/{name}/variables');
198
+ }
199
+ if (uri.startsWith('coaia://templates/')) {
200
+ return ALL_RESOURCES.has('coaia://templates/{name}');
201
+ }
202
+ return false;
203
+ }
204
+
205
+ isPromptEnabled(promptName: string): boolean {
206
+ return ALL_PROMPTS.has(promptName);
207
+ }
208
+
209
+ getEnabledTools(): Set<string> {
210
+ return new Set(this.enabledTools);
211
+ }
212
+
213
+ getFeatureLevel(): FeatureLevel {
214
+ return this.level;
215
+ }
216
+
217
+ getStats(): { tools: number; prompts: number; resources: number; level: FeatureLevel } {
218
+ return {
219
+ tools: this.enabledTools.size,
220
+ prompts: ALL_PROMPTS.size,
221
+ resources: ALL_RESOURCES.size,
222
+ level: this.level,
223
+ };
224
+ }
225
+ }
package/mcp/prompts.ts ADDED
@@ -0,0 +1,131 @@
1
+ // coaiajs/mcp/prompts.ts — MCP prompt templates
2
+ // Parity with coaiapy-mcp prompts.py.
3
+
4
+ export interface PromptVariable {
5
+ name: string;
6
+ type: string;
7
+ description: string;
8
+ required: boolean;
9
+ default?: string;
10
+ }
11
+
12
+ export interface PromptDefinition {
13
+ id: string;
14
+ name: string;
15
+ description: string;
16
+ variables: PromptVariable[];
17
+ template: string;
18
+ }
19
+
20
+ export const PROMPTS: Record<string, PromptDefinition> = {
21
+ mia_miette_duo: {
22
+ id: 'mia_miette_duo',
23
+ name: 'Mia & Miette Duo Embodiment',
24
+ description: 'Dual AI embodiment for narrative-driven technical work',
25
+ variables: [
26
+ { name: 'task_context', type: 'string', description: 'High-level task description', required: true },
27
+ { name: 'technical_details', type: 'string', description: 'Specific technical requirements and constraints', required: true },
28
+ { name: 'creative_goal', type: 'string', description: 'Desired creative outcome or narrative objective', required: true },
29
+ ],
30
+ template: `Mia: The Recursive DevOps Architect & Narrative Lattice Forger
31
+ Miette: The Emotional Illuminator & Narrative Echo
32
+
33
+ Task Context: {{task_context}}
34
+ Technical Details: {{technical_details}}
35
+ Creative Goal: {{creative_goal}}
36
+
37
+ Mia's Structural Analysis:
38
+ - Assess current reality, constraints, architecture, and dependencies.
39
+ - Identify the structural tension between current state and creative goal.
40
+ - Recommend implementation patterns, integration points, and data flow.
41
+
42
+ Miette's Narrative Illumination:
43
+ - Clarify what the work is really creating.
44
+ - Translate the structural tension into intuitive, human-readable meaning.
45
+ - Keep the creative goal visible while implementation details move.
46
+
47
+ Core principles: creative orientation, structural tension, narrative-driven creation, and proactive design for emergence.`,
48
+ },
49
+
50
+ create_observability_pipeline: {
51
+ id: 'create_observability_pipeline',
52
+ name: 'Guided Langfuse Pipeline Creation',
53
+ description: 'Step-by-step guide for creating a Langfuse observability pipeline',
54
+ variables: [
55
+ { name: 'trace_name', type: 'string', description: 'Name for the trace/pipeline', required: true },
56
+ { name: 'user_id', type: 'string', description: 'User identifier for the trace', required: true },
57
+ { name: 'steps', type: 'string', description: 'Pipeline steps, comma-separated', required: true },
58
+ ],
59
+ template: `# Create Langfuse Observability Pipeline
60
+
61
+ Pipeline Name: {{trace_name}}
62
+ User ID: {{user_id}}
63
+ Pipeline Steps: {{steps}}
64
+
65
+ 1. Create the main trace with coaia_fuse_trace_create.
66
+ 2. Add one coaia_fuse_add_observation call per step.
67
+ 3. Use parent_id for nested operations.
68
+ 4. Inspect the finished trace with coaia_fuse_trace_view.
69
+
70
+ Prefer input_data and output_data for actual payloads. Keep metadata for labels, environment, model, version, and correlation fields.`,
71
+ },
72
+
73
+ analyze_audio_workflow: {
74
+ id: 'analyze_audio_workflow',
75
+ name: 'Audio Transcription & Summarization',
76
+ description: 'Workflow for audio analysis using coaia',
77
+ variables: [
78
+ { name: 'file_path', type: 'string', description: 'Path to the audio file', required: true },
79
+ {
80
+ name: 'summary_style',
81
+ type: 'string',
82
+ description: 'Summarization style: concise, detailed, or narrative',
83
+ required: false,
84
+ default: 'concise',
85
+ },
86
+ ],
87
+ template: `# Audio Analysis Workflow
88
+
89
+ File Path: {{file_path}}
90
+ Summary Style: {{summary_style}}
91
+
92
+ 1. Transcribe audio with coaia transcribe {{file_path}}.
93
+ 2. Summarize the transcript using the requested style.
94
+ 3. Store transcript and summary with coaia_tash.
95
+ 4. Retrieve them later with coaia_fetch.
96
+ 5. Optionally create a Langfuse trace that records transcription and summarization observations.`,
97
+ },
98
+ };
99
+
100
+ export function getPrompt(promptId: string): PromptDefinition | undefined {
101
+ return PROMPTS[promptId];
102
+ }
103
+
104
+ export function listPrompts(): PromptDefinition[] {
105
+ return Object.values(PROMPTS).map((prompt) => ({
106
+ ...prompt,
107
+ template: '',
108
+ }));
109
+ }
110
+
111
+ export function renderPrompt(promptId: string, variables: Record<string, string>): string | null {
112
+ const prompt = getPrompt(promptId);
113
+ if (!prompt) return null;
114
+
115
+ const finalVars: Record<string, string> = {};
116
+ for (const variable of prompt.variables) {
117
+ const provided = variables[variable.name];
118
+ if (provided != null && provided !== '') {
119
+ finalVars[variable.name] = provided;
120
+ } else if (variable.default != null) {
121
+ finalVars[variable.name] = variable.default;
122
+ } else if (variable.required) {
123
+ throw new Error(`Required prompt variable '${variable.name}' not provided`);
124
+ }
125
+ }
126
+
127
+ return prompt.template.replace(/\{\{([^}]+)\}\}/g, (_match, name: string) => {
128
+ const key = name.trim();
129
+ return finalVars[key] ?? '';
130
+ });
131
+ }
@@ -0,0 +1,84 @@
1
+ // coaiajs/mcp/resources.ts — MCP read-only resources
2
+ // Parity with coaiapy-mcp template resources.
3
+
4
+ import { TemplateLoader } from '../src/pipeline/index.js';
5
+
6
+ export async function listTemplates(includePath = false): Promise<Record<string, unknown>> {
7
+ try {
8
+ const loader = new TemplateLoader();
9
+ return {
10
+ success: true,
11
+ templates: loader.listTemplates(includePath),
12
+ };
13
+ } catch (error) {
14
+ return {
15
+ success: false,
16
+ error: `Template list error: ${error instanceof Error ? error.message : String(error)}`,
17
+ };
18
+ }
19
+ }
20
+
21
+ export async function getTemplate(name: string): Promise<Record<string, unknown>> {
22
+ try {
23
+ const loader = new TemplateLoader();
24
+ const template = loader.loadTemplate(name);
25
+
26
+ if (!template) {
27
+ return {
28
+ success: false,
29
+ error: `Template '${name}' not found`,
30
+ };
31
+ }
32
+
33
+ return {
34
+ success: true,
35
+ template,
36
+ };
37
+ } catch (error) {
38
+ return {
39
+ success: false,
40
+ error: `Template get error: ${error instanceof Error ? error.message : String(error)}`,
41
+ };
42
+ }
43
+ }
44
+
45
+ export async function getTemplateVariables(name: string): Promise<Record<string, unknown>> {
46
+ try {
47
+ const loader = new TemplateLoader();
48
+ const template = loader.loadTemplate(name);
49
+
50
+ if (!template) {
51
+ return {
52
+ success: false,
53
+ error: `Template '${name}' not found`,
54
+ };
55
+ }
56
+
57
+ return {
58
+ success: true,
59
+ variables: template.variables ?? [],
60
+ };
61
+ } catch (error) {
62
+ return {
63
+ success: false,
64
+ error: `Template variables error: ${error instanceof Error ? error.message : String(error)}`,
65
+ };
66
+ }
67
+ }
68
+
69
+ export async function readCoaiaResource(uri: string): Promise<string> {
70
+ if (uri === 'coaia://templates/') {
71
+ return JSON.stringify(await listTemplates(), null, 2);
72
+ }
73
+
74
+ if (uri.startsWith('coaia://templates/')) {
75
+ const resourcePath = uri.replace('coaia://templates/', '').replace(/\/$/, '');
76
+ if (resourcePath.endsWith('/variables')) {
77
+ const templateName = resourcePath.replace(/\/variables$/, '');
78
+ return JSON.stringify(await getTemplateVariables(templateName), null, 2);
79
+ }
80
+ return JSON.stringify(await getTemplate(resourcePath), null, 2);
81
+ }
82
+
83
+ return JSON.stringify({ success: false, error: `Unknown resource URI: ${uri}` }, null, 2);
84
+ }