@jaypie/mcp 0.7.9 → 0.7.11

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.
@@ -2,7 +2,7 @@ import { fabricService } from '@jaypie/fabric';
2
2
  import * as fs from 'node:fs/promises';
3
3
  import * as path from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
- import { getDatadogCredentials, searchDatadogRum, queryDatadogMetrics, getDatadogSyntheticResults, listDatadogSynthetics, listDatadogMonitors, aggregateDatadogLogs, searchDatadogLogs } from './datadog.js';
5
+ import { getDatadogCredentials, searchDatadogRum, queryDatadogMetrics, getDatadogSyntheticResults, listDatadogSynthetics, listDatadogMonitors, aggregateDatadogLogs, searchDatadogLogs, validateDatadogSetup } from './datadog.js';
6
6
  export { buildDatadogQuery, validateDatadogCredentials } from './datadog.js';
7
7
 
8
8
  /**
@@ -21,18 +21,99 @@ const datadogService = fabricService({
21
21
  alias: "datadog",
22
22
  description: "Access Datadog observability data. Commands: logs, log_analytics, monitors, synthetics, metrics, rum. Call with no args for help.",
23
23
  input: {
24
+ aggregation: {
25
+ description: "Aggregation type for log_analytics (count, avg, sum, min, max, cardinality)",
26
+ required: false,
27
+ type: String,
28
+ },
24
29
  command: {
25
- description: "Command to execute (omit for help)",
30
+ description: "Command to execute: logs, log_analytics, monitors, synthetics, metrics, rum (omit for help)",
26
31
  required: false,
27
32
  type: String,
28
33
  },
29
- input: {
30
- description: "Command parameters",
34
+ env: {
35
+ description: "Environment filter (e.g., production, staging)",
31
36
  required: false,
32
- type: Object,
37
+ type: String,
38
+ },
39
+ from: {
40
+ description: "Start time (e.g., now-1h, now-15m, now-1d, or ISO 8601 timestamp)",
41
+ required: false,
42
+ type: String,
43
+ },
44
+ groupBy: {
45
+ description: "Fields to group by for log_analytics (comma-separated, e.g., service,status)",
46
+ required: false,
47
+ type: String,
48
+ },
49
+ limit: {
50
+ description: "Maximum number of results to return",
51
+ required: false,
52
+ type: Number,
53
+ },
54
+ metric: {
55
+ description: "Metric field for aggregation (e.g., @duration)",
56
+ required: false,
57
+ type: String,
58
+ },
59
+ monitorTags: {
60
+ description: "Monitor tags filter (comma-separated)",
61
+ required: false,
62
+ type: String,
63
+ },
64
+ name: {
65
+ description: "Name filter for monitors",
66
+ required: false,
67
+ type: String,
68
+ },
69
+ query: {
70
+ description: 'Datadog query string (e.g., status:error, @lambda.arn:"arn:aws:...")',
71
+ required: false,
72
+ type: String,
73
+ },
74
+ service: {
75
+ description: "Service name filter",
76
+ required: false,
77
+ type: String,
78
+ },
79
+ sort: {
80
+ description: "Sort order (timestamp or -timestamp)",
81
+ required: false,
82
+ type: String,
83
+ },
84
+ source: {
85
+ description: "Log source filter (default: lambda)",
86
+ required: false,
87
+ type: String,
88
+ },
89
+ status: {
90
+ description: "Monitor status filter (comma-separated: Alert, Warn, No Data, OK)",
91
+ required: false,
92
+ type: String,
93
+ },
94
+ tags: {
95
+ description: "Tags filter (comma-separated)",
96
+ required: false,
97
+ type: String,
98
+ },
99
+ testId: {
100
+ description: "Synthetic test ID for getting results",
101
+ required: false,
102
+ type: String,
103
+ },
104
+ to: {
105
+ description: "End time (e.g., now, or ISO 8601 timestamp)",
106
+ required: false,
107
+ type: String,
108
+ },
109
+ type: {
110
+ description: "Synthetic test type filter (api or browser)",
111
+ required: false,
112
+ type: String,
33
113
  },
34
114
  },
35
- service: async ({ command, input: params, }) => {
115
+ service: async (params) => {
116
+ const { command } = params;
36
117
  if (!command || command === "help") {
37
118
  return getHelp();
38
119
  }
@@ -40,75 +121,90 @@ const datadogService = fabricService({
40
121
  if (!credentials) {
41
122
  throw new Error("Datadog credentials not found. Set DATADOG_API_KEY and DATADOG_APP_KEY.");
42
123
  }
43
- const p = params || {};
124
+ // Helper to parse comma-separated strings into arrays
125
+ const parseArray = (value) => {
126
+ if (!value)
127
+ return undefined;
128
+ return value.split(",").map((s) => s.trim());
129
+ };
44
130
  switch (command) {
131
+ case "validate": {
132
+ return validateDatadogSetup();
133
+ }
45
134
  case "logs": {
46
135
  const result = await searchDatadogLogs(credentials, {
47
- env: p.env,
48
- from: p.from,
49
- limit: p.limit,
50
- query: p.query,
51
- service: p.service,
52
- sort: p.sort,
53
- source: p.source,
54
- to: p.to,
136
+ env: params.env,
137
+ from: params.from,
138
+ limit: params.limit,
139
+ query: params.query,
140
+ service: params.service,
141
+ sort: params.sort,
142
+ source: params.source,
143
+ to: params.to,
55
144
  }, log);
56
145
  if (!result.success)
57
146
  throw new Error(result.error);
58
147
  return result;
59
148
  }
60
149
  case "log_analytics": {
61
- if (!p.groupBy || p.groupBy.length === 0) {
62
- throw new Error("groupBy is required (array of field names)");
150
+ const groupByArray = parseArray(params.groupBy);
151
+ if (!groupByArray || groupByArray.length === 0) {
152
+ throw new Error("groupBy is required (comma-separated field names, e.g., service,status)");
63
153
  }
64
- const compute = p.aggregation
65
- ? [{ aggregation: p.aggregation, metric: p.metric }]
154
+ const compute = params.aggregation
155
+ ? [
156
+ {
157
+ aggregation: params.aggregation,
158
+ metric: params.metric,
159
+ },
160
+ ]
66
161
  : [{ aggregation: "count" }];
67
162
  const result = await aggregateDatadogLogs(credentials, {
68
163
  compute,
69
- env: p.env,
70
- from: p.from,
71
- groupBy: p.groupBy,
72
- query: p.query,
73
- service: p.service,
74
- source: p.source,
75
- to: p.to,
164
+ env: params.env,
165
+ from: params.from,
166
+ groupBy: groupByArray,
167
+ query: params.query,
168
+ service: params.service,
169
+ source: params.source,
170
+ to: params.to,
76
171
  }, log);
77
172
  if (!result.success)
78
173
  throw new Error(result.error);
79
174
  return result;
80
175
  }
81
176
  case "monitors": {
177
+ const statusArray = parseArray(params.status);
82
178
  const result = await listDatadogMonitors(credentials, {
83
- monitorTags: p.monitorTags,
84
- name: p.name,
85
- status: p.status,
86
- tags: p.tags,
179
+ monitorTags: parseArray(params.monitorTags),
180
+ name: params.name,
181
+ status: statusArray,
182
+ tags: parseArray(params.tags),
87
183
  }, log);
88
184
  if (!result.success)
89
185
  throw new Error(result.error);
90
186
  return result;
91
187
  }
92
188
  case "synthetics": {
93
- if (p.testId) {
94
- const result = await getDatadogSyntheticResults(credentials, p.testId, log);
189
+ if (params.testId) {
190
+ const result = await getDatadogSyntheticResults(credentials, params.testId, log);
95
191
  if (!result.success)
96
192
  throw new Error(result.error);
97
193
  return result;
98
194
  }
99
195
  const result = await listDatadogSynthetics(credentials, {
100
- tags: p.tags,
101
- type: p.type,
196
+ tags: parseArray(params.tags),
197
+ type: params.type,
102
198
  }, log);
103
199
  if (!result.success)
104
200
  throw new Error(result.error);
105
201
  return result;
106
202
  }
107
203
  case "metrics": {
108
- if (!p.query)
204
+ if (!params.query)
109
205
  throw new Error("query is required for metrics");
110
206
  const now = Math.floor(Date.now() / 1000);
111
- const fromStr = p.from || "1h";
207
+ const fromStr = params.from || "1h";
112
208
  let fromTs;
113
209
  if (fromStr.match(/^\d+$/)) {
114
210
  fromTs = parseInt(fromStr, 10);
@@ -128,19 +224,19 @@ const datadogService = fabricService({
128
224
  else {
129
225
  fromTs = now - 3600;
130
226
  }
131
- const toStr = p.to || "now";
227
+ const toStr = params.to || "now";
132
228
  const toTs = toStr === "now" ? now : parseInt(toStr, 10);
133
- const result = await queryDatadogMetrics(credentials, { from: fromTs, query: p.query, to: toTs }, log);
229
+ const result = await queryDatadogMetrics(credentials, { from: fromTs, query: params.query, to: toTs }, log);
134
230
  if (!result.success)
135
231
  throw new Error(result.error);
136
232
  return result;
137
233
  }
138
234
  case "rum": {
139
235
  const result = await searchDatadogRum(credentials, {
140
- from: p.from,
141
- limit: p.limit,
142
- query: p.query,
143
- to: p.to,
236
+ from: params.from,
237
+ limit: params.limit,
238
+ query: params.query,
239
+ to: params.to,
144
240
  }, log);
145
241
  if (!result.success)
146
242
  throw new Error(result.error);
@@ -152,5 +248,5 @@ const datadogService = fabricService({
152
248
  },
153
249
  });
154
250
 
155
- export { aggregateDatadogLogs, datadogService, getDatadogCredentials, getDatadogSyntheticResults, listDatadogMonitors, listDatadogSynthetics, queryDatadogMetrics, searchDatadogLogs, searchDatadogRum };
251
+ export { aggregateDatadogLogs, datadogService, getDatadogCredentials, getDatadogSyntheticResults, listDatadogMonitors, listDatadogSynthetics, queryDatadogMetrics, searchDatadogLogs, searchDatadogRum, validateDatadogSetup };
156
252
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/suites/datadog/index.ts"],"sourcesContent":["/**\n * Datadog Suite - Unified Datadog observability access\n */\nimport { fabricService } from \"@jaypie/fabric\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport {\n aggregateDatadogLogs,\n getDatadogCredentials,\n getDatadogSyntheticResults,\n listDatadogMonitors,\n listDatadogSynthetics,\n queryDatadogMetrics,\n searchDatadogLogs,\n searchDatadogRum,\n} from \"./datadog.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// Silent logger for direct execution\nconst log = {\n error: () => {},\n info: () => {},\n};\n\nasync function getHelp(): Promise<string> {\n return fs.readFile(path.join(__dirname, \"help.md\"), \"utf-8\");\n}\n\n// Input type for the unified Datadog service\ninterface DatadogInput {\n aggregation?: \"count\" | \"avg\" | \"sum\" | \"min\" | \"max\" | \"cardinality\";\n env?: string;\n from?: string;\n groupBy?: string[];\n limit?: number;\n metric?: string;\n monitorTags?: string[];\n name?: string;\n query?: string;\n service?: string;\n sort?: \"timestamp\" | \"-timestamp\";\n source?: string;\n status?: (\"Alert\" | \"Warn\" | \"No Data\" | \"OK\")[];\n tags?: string[];\n testId?: string;\n to?: string;\n type?: \"api\" | \"browser\";\n}\n\nexport const datadogService = fabricService({\n alias: \"datadog\",\n description:\n \"Access Datadog observability data. Commands: logs, log_analytics, monitors, synthetics, metrics, rum. Call with no args for help.\",\n input: {\n command: {\n description: \"Command to execute (omit for help)\",\n required: false,\n type: String,\n },\n input: {\n description: \"Command parameters\",\n required: false,\n type: Object,\n },\n },\n service: async ({\n command,\n input: params,\n }: {\n command?: string;\n input?: DatadogInput;\n }) => {\n if (!command || command === \"help\") {\n return getHelp();\n }\n\n const credentials = getDatadogCredentials();\n if (!credentials) {\n throw new Error(\n \"Datadog credentials not found. Set DATADOG_API_KEY and DATADOG_APP_KEY.\",\n );\n }\n\n const p = params || {};\n\n switch (command) {\n case \"logs\": {\n const result = await searchDatadogLogs(\n credentials,\n {\n env: p.env,\n from: p.from,\n limit: p.limit,\n query: p.query,\n service: p.service,\n sort: p.sort,\n source: p.source,\n to: p.to,\n },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n case \"log_analytics\": {\n if (!p.groupBy || p.groupBy.length === 0) {\n throw new Error(\"groupBy is required (array of field names)\");\n }\n const compute = p.aggregation\n ? [{ aggregation: p.aggregation, metric: p.metric }]\n : [{ aggregation: \"count\" as const }];\n const result = await aggregateDatadogLogs(\n credentials,\n {\n compute,\n env: p.env,\n from: p.from,\n groupBy: p.groupBy,\n query: p.query,\n service: p.service,\n source: p.source,\n to: p.to,\n },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n case \"monitors\": {\n const result = await listDatadogMonitors(\n credentials,\n {\n monitorTags: p.monitorTags,\n name: p.name,\n status: p.status,\n tags: p.tags,\n },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n case \"synthetics\": {\n if (p.testId) {\n const result = await getDatadogSyntheticResults(\n credentials,\n p.testId,\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n const result = await listDatadogSynthetics(\n credentials,\n {\n tags: p.tags,\n type: p.type,\n },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n case \"metrics\": {\n if (!p.query) throw new Error(\"query is required for metrics\");\n const now = Math.floor(Date.now() / 1000);\n const fromStr = p.from || \"1h\";\n let fromTs: number;\n if (fromStr.match(/^\\d+$/)) {\n fromTs = parseInt(fromStr, 10);\n } else if (fromStr.match(/^(\\d+)h$/)) {\n const hours = parseInt(fromStr.match(/^(\\d+)h$/)![1], 10);\n fromTs = now - hours * 3600;\n } else if (fromStr.match(/^(\\d+)m$/)) {\n const minutes = parseInt(fromStr.match(/^(\\d+)m$/)![1], 10);\n fromTs = now - minutes * 60;\n } else if (fromStr.match(/^(\\d+)d$/)) {\n const days = parseInt(fromStr.match(/^(\\d+)d$/)![1], 10);\n fromTs = now - days * 86400;\n } else {\n fromTs = now - 3600;\n }\n const toStr = p.to || \"now\";\n const toTs = toStr === \"now\" ? now : parseInt(toStr, 10);\n const result = await queryDatadogMetrics(\n credentials,\n { from: fromTs, query: p.query, to: toTs },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n case \"rum\": {\n const result = await searchDatadogRum(\n credentials,\n {\n from: p.from,\n limit: p.limit,\n query: p.query,\n to: p.to,\n },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n default:\n throw new Error(`Unknown command: ${command}. Use datadog() for help.`);\n }\n },\n});\n\n// Re-export types and functions for testing\nexport * from \"./datadog.js\";\n"],"names":["__dirname"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/suites/datadog/index.ts"],"sourcesContent":["/**\n * Datadog Suite - Unified Datadog observability access\n */\nimport { fabricService } from \"@jaypie/fabric\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport {\n aggregateDatadogLogs,\n getDatadogCredentials,\n getDatadogSyntheticResults,\n listDatadogMonitors,\n listDatadogSynthetics,\n queryDatadogMetrics,\n searchDatadogLogs,\n searchDatadogRum,\n validateDatadogSetup,\n} from \"./datadog.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// Silent logger for direct execution\nconst log = {\n error: () => {},\n info: () => {},\n};\n\nasync function getHelp(): Promise<string> {\n return fs.readFile(path.join(__dirname, \"help.md\"), \"utf-8\");\n}\n\n// Flattened input type for the unified Datadog service\ninterface DatadogInput {\n aggregation?: string;\n command?: string;\n env?: string;\n from?: string;\n groupBy?: string;\n limit?: number;\n metric?: string;\n monitorTags?: string;\n name?: string;\n query?: string;\n service?: string;\n sort?: string;\n source?: string;\n status?: string;\n tags?: string;\n testId?: string;\n to?: string;\n type?: string;\n}\n\nexport const datadogService = fabricService({\n alias: \"datadog\",\n description:\n \"Access Datadog observability data. Commands: logs, log_analytics, monitors, synthetics, metrics, rum. Call with no args for help.\",\n input: {\n aggregation: {\n description:\n \"Aggregation type for log_analytics (count, avg, sum, min, max, cardinality)\",\n required: false,\n type: String,\n },\n command: {\n description:\n \"Command to execute: logs, log_analytics, monitors, synthetics, metrics, rum (omit for help)\",\n required: false,\n type: String,\n },\n env: {\n description: \"Environment filter (e.g., production, staging)\",\n required: false,\n type: String,\n },\n from: {\n description:\n \"Start time (e.g., now-1h, now-15m, now-1d, or ISO 8601 timestamp)\",\n required: false,\n type: String,\n },\n groupBy: {\n description:\n \"Fields to group by for log_analytics (comma-separated, e.g., service,status)\",\n required: false,\n type: String,\n },\n limit: {\n description: \"Maximum number of results to return\",\n required: false,\n type: Number,\n },\n metric: {\n description: \"Metric field for aggregation (e.g., @duration)\",\n required: false,\n type: String,\n },\n monitorTags: {\n description: \"Monitor tags filter (comma-separated)\",\n required: false,\n type: String,\n },\n name: {\n description: \"Name filter for monitors\",\n required: false,\n type: String,\n },\n query: {\n description:\n 'Datadog query string (e.g., status:error, @lambda.arn:\"arn:aws:...\")',\n required: false,\n type: String,\n },\n service: {\n description: \"Service name filter\",\n required: false,\n type: String,\n },\n sort: {\n description: \"Sort order (timestamp or -timestamp)\",\n required: false,\n type: String,\n },\n source: {\n description: \"Log source filter (default: lambda)\",\n required: false,\n type: String,\n },\n status: {\n description:\n \"Monitor status filter (comma-separated: Alert, Warn, No Data, OK)\",\n required: false,\n type: String,\n },\n tags: {\n description: \"Tags filter (comma-separated)\",\n required: false,\n type: String,\n },\n testId: {\n description: \"Synthetic test ID for getting results\",\n required: false,\n type: String,\n },\n to: {\n description: \"End time (e.g., now, or ISO 8601 timestamp)\",\n required: false,\n type: String,\n },\n type: {\n description: \"Synthetic test type filter (api or browser)\",\n required: false,\n type: String,\n },\n },\n service: async (params: DatadogInput) => {\n const { command } = params;\n\n if (!command || command === \"help\") {\n return getHelp();\n }\n\n const credentials = getDatadogCredentials();\n if (!credentials) {\n throw new Error(\n \"Datadog credentials not found. Set DATADOG_API_KEY and DATADOG_APP_KEY.\",\n );\n }\n\n // Helper to parse comma-separated strings into arrays\n const parseArray = (value?: string): string[] | undefined => {\n if (!value) return undefined;\n return value.split(\",\").map((s) => s.trim());\n };\n\n switch (command) {\n case \"validate\": {\n return validateDatadogSetup();\n }\n\n case \"logs\": {\n const result = await searchDatadogLogs(\n credentials,\n {\n env: params.env,\n from: params.from,\n limit: params.limit,\n query: params.query,\n service: params.service,\n sort: params.sort as \"timestamp\" | \"-timestamp\" | undefined,\n source: params.source,\n to: params.to,\n },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n case \"log_analytics\": {\n const groupByArray = parseArray(params.groupBy);\n if (!groupByArray || groupByArray.length === 0) {\n throw new Error(\n \"groupBy is required (comma-separated field names, e.g., service,status)\",\n );\n }\n const compute = params.aggregation\n ? [\n {\n aggregation: params.aggregation as\n | \"count\"\n | \"avg\"\n | \"sum\"\n | \"min\"\n | \"max\"\n | \"cardinality\",\n metric: params.metric,\n },\n ]\n : [{ aggregation: \"count\" as const }];\n const result = await aggregateDatadogLogs(\n credentials,\n {\n compute,\n env: params.env,\n from: params.from,\n groupBy: groupByArray,\n query: params.query,\n service: params.service,\n source: params.source,\n to: params.to,\n },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n case \"monitors\": {\n const statusArray = parseArray(params.status) as\n | (\"Alert\" | \"Warn\" | \"No Data\" | \"OK\")[]\n | undefined;\n const result = await listDatadogMonitors(\n credentials,\n {\n monitorTags: parseArray(params.monitorTags),\n name: params.name,\n status: statusArray,\n tags: parseArray(params.tags),\n },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n case \"synthetics\": {\n if (params.testId) {\n const result = await getDatadogSyntheticResults(\n credentials,\n params.testId,\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n const result = await listDatadogSynthetics(\n credentials,\n {\n tags: parseArray(params.tags),\n type: params.type as \"api\" | \"browser\" | undefined,\n },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n case \"metrics\": {\n if (!params.query) throw new Error(\"query is required for metrics\");\n const now = Math.floor(Date.now() / 1000);\n const fromStr = params.from || \"1h\";\n let fromTs: number;\n if (fromStr.match(/^\\d+$/)) {\n fromTs = parseInt(fromStr, 10);\n } else if (fromStr.match(/^(\\d+)h$/)) {\n const hours = parseInt(fromStr.match(/^(\\d+)h$/)![1], 10);\n fromTs = now - hours * 3600;\n } else if (fromStr.match(/^(\\d+)m$/)) {\n const minutes = parseInt(fromStr.match(/^(\\d+)m$/)![1], 10);\n fromTs = now - minutes * 60;\n } else if (fromStr.match(/^(\\d+)d$/)) {\n const days = parseInt(fromStr.match(/^(\\d+)d$/)![1], 10);\n fromTs = now - days * 86400;\n } else {\n fromTs = now - 3600;\n }\n const toStr = params.to || \"now\";\n const toTs = toStr === \"now\" ? now : parseInt(toStr, 10);\n const result = await queryDatadogMetrics(\n credentials,\n { from: fromTs, query: params.query, to: toTs },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n case \"rum\": {\n const result = await searchDatadogRum(\n credentials,\n {\n from: params.from,\n limit: params.limit,\n query: params.query,\n to: params.to,\n },\n log,\n );\n if (!result.success) throw new Error(result.error);\n return result;\n }\n\n default:\n throw new Error(`Unknown command: ${command}. Use datadog() for help.`);\n }\n },\n});\n\n// Re-export types and functions for testing\nexport * from \"./datadog.js\";\n"],"names":["__dirname"],"mappings":"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"}
@@ -9,7 +9,7 @@ import { gt } from 'semver';
9
9
  /**
10
10
  * Docs Suite - Documentation services (skill, version, release_notes)
11
11
  */
12
- const BUILD_VERSION_STRING = "@jaypie/mcp@0.7.9#3c1d1abd"
12
+ const BUILD_VERSION_STRING = "@jaypie/mcp@0.7.11#d7ce271c"
13
13
  ;
14
14
  const __filename$1 = fileURLToPath(import.meta.url);
15
15
  const __dirname$1 = path.dirname(__filename$1);
@@ -95,6 +95,44 @@ function filterReleaseNotesSince(notes, sinceVersion) {
95
95
  // =============================================================================
96
96
  // SKILL SERVICE
97
97
  // =============================================================================
98
+ /**
99
+ * Generate alternative spellings for plural/singular matching
100
+ */
101
+ function getAlternativeSpellings(alias) {
102
+ const alternatives = [];
103
+ if (alias.endsWith("es")) {
104
+ // "indexes" -> try "indexe" and "index"
105
+ alternatives.push(alias.slice(0, -1)); // Remove "s" -> "indexe"
106
+ alternatives.push(alias.slice(0, -2)); // Remove "es" -> "index"
107
+ }
108
+ else if (alias.endsWith("s")) {
109
+ // "skills" -> try "skill"
110
+ alternatives.push(alias.slice(0, -1)); // Remove "s" -> "skill"
111
+ }
112
+ else {
113
+ // "fish" -> try "fishs" and "fishes"
114
+ alternatives.push(alias + "s");
115
+ alternatives.push(alias + "es");
116
+ }
117
+ return alternatives;
118
+ }
119
+ /**
120
+ * Add alias to frontmatter indicating the canonical skill name
121
+ */
122
+ function addAliasToFrontmatter(content, matchedAlias) {
123
+ if (content.startsWith("---")) {
124
+ // Find the end of frontmatter
125
+ const endIndex = content.indexOf("---", 3);
126
+ if (endIndex !== -1) {
127
+ // Insert alias before the closing ---
128
+ const beforeClose = content.slice(0, endIndex);
129
+ const afterClose = content.slice(endIndex);
130
+ return `${beforeClose}alias: ${matchedAlias}\n${afterClose}`;
131
+ }
132
+ }
133
+ // No frontmatter exists, create one
134
+ return `---\nalias: ${matchedAlias}\n---\n\n${content}`;
135
+ }
98
136
  const skillService = fabricService({
99
137
  alias: "skill",
100
138
  description: "Access Jaypie development documentation. Pass a skill alias (e.g., 'aws', 'tests', 'errors') to get that documentation. Pass 'index' or no argument to list all available skills.",
@@ -123,13 +161,31 @@ const skillService = fabricService({
123
161
  }
124
162
  return `# Jaypie Skills\n\n## Available Skills\n\n${skillList}`;
125
163
  }
126
- const skill = await skillStore.get(alias);
164
+ // Try exact match first
165
+ let skill = await skillStore.get(alias);
166
+ let matchedAlias = alias;
167
+ // If no exact match, try alternative spellings (plural/singular)
168
+ if (!skill) {
169
+ const alternatives = getAlternativeSpellings(alias);
170
+ for (const alt of alternatives) {
171
+ skill = await skillStore.get(alt);
172
+ if (skill) {
173
+ matchedAlias = alt;
174
+ break;
175
+ }
176
+ }
177
+ }
127
178
  if (!skill) {
128
179
  throw new Error(`Skill "${alias}" not found. Use skill("index") to list available skills.`);
129
180
  }
130
181
  // Return raw file content for non-index skills (preserve frontmatter)
131
- const skillPath = path.join(SKILLS_PATH, `${alias}.md`);
132
- return fs.readFile(skillPath, "utf-8");
182
+ const skillPath = path.join(SKILLS_PATH, `${matchedAlias}.md`);
183
+ let content = await fs.readFile(skillPath, "utf-8");
184
+ // If we matched via alternative spelling, add alias to indicate canonical name
185
+ if (matchedAlias !== alias) {
186
+ content = addAliasToFrontmatter(content, matchedAlias);
187
+ }
188
+ return content;
133
189
  },
134
190
  });
135
191
  // =============================================================================
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/suites/docs/index.ts"],"sourcesContent":["/**\n * Docs Suite - Documentation services (skill, version, release_notes)\n */\nimport { fabricService } from \"@jaypie/fabric\";\nimport {\n createMarkdownStore,\n isValidAlias,\n normalizeAlias,\n type SkillRecord,\n} from \"@jaypie/tildeskill\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport matter from \"gray-matter\";\nimport { gt } from \"semver\";\n\n// Build-time constants\ndeclare const __BUILD_VERSION_STRING__: string;\nconst BUILD_VERSION_STRING =\n typeof __BUILD_VERSION_STRING__ !== \"undefined\"\n ? __BUILD_VERSION_STRING__\n : \"@jaypie/mcp@0.0.0\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n// From dist/suites/docs/, go up 3 levels to package root where skills/ and release-notes/ live\nconst RELEASE_NOTES_PATH = path.join(\n __dirname,\n \"..\",\n \"..\",\n \"..\",\n \"release-notes\",\n);\nconst SKILLS_PATH = path.join(__dirname, \"..\", \"..\", \"..\", \"skills\");\n\n// Create skill store using tildeskill\nconst skillStore = createMarkdownStore({ path: SKILLS_PATH });\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\ninterface ReleaseNoteFrontMatter {\n date?: string;\n summary?: string;\n version?: string;\n}\n\nasync function parseReleaseNoteFile(filePath: string): Promise<{\n date?: string;\n filename: string;\n summary?: string;\n version?: string;\n}> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n const filename = path.basename(filePath, \".md\");\n\n if (content.startsWith(\"---\")) {\n const parsed = matter(content);\n const frontMatter = parsed.data as ReleaseNoteFrontMatter;\n return {\n date: frontMatter.date,\n filename,\n summary: frontMatter.summary,\n version: frontMatter.version || filename,\n };\n }\n\n return { filename, version: filename };\n } catch {\n return { filename: path.basename(filePath, \".md\") };\n }\n}\n\nfunction formatReleaseNoteListItem(note: {\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}): string {\n const { date, packageName, summary, version } = note;\n const parts = [`* ${packageName}@${version}`];\n\n if (date) {\n parts.push(`(${date})`);\n }\n\n if (summary) {\n parts.push(`- ${summary}`);\n }\n\n return parts.join(\" \");\n}\n\nfunction formatSkillListItem(skill: SkillRecord): string {\n const { alias, description } = skill;\n if (description) {\n return `* ${alias} - ${description}`;\n }\n return `* ${alias}`;\n}\n\nasync function getPackageReleaseNotes(packageName: string): Promise<\n Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>\n> {\n const packageDir = path.join(RELEASE_NOTES_PATH, packageName);\n try {\n const files = await fs.readdir(packageDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const notes = await Promise.all(\n mdFiles.map(async (file) => {\n const parsed = await parseReleaseNoteFile(path.join(packageDir, file));\n return { ...parsed, packageName };\n }),\n );\n\n return notes.sort((a, b) => {\n if (!a.version || !b.version) return 0;\n try {\n return gt(a.version, b.version) ? -1 : 1;\n } catch {\n return b.version.localeCompare(a.version);\n }\n });\n } catch {\n return [];\n }\n}\n\nfunction filterReleaseNotesSince(\n notes: Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>,\n sinceVersion: string,\n): Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}> {\n return notes.filter((note) => {\n if (!note.version) return false;\n try {\n return gt(note.version, sinceVersion);\n } catch {\n return false;\n }\n });\n}\n\n// =============================================================================\n// SKILL SERVICE\n// =============================================================================\n\nexport const skillService = fabricService({\n alias: \"skill\",\n description:\n \"Access Jaypie development documentation. Pass a skill alias (e.g., 'aws', 'tests', 'errors') to get that documentation. Pass 'index' or no argument to list all available skills.\",\n input: {\n alias: {\n description:\n \"Skill alias (e.g., 'aws', 'tests'). Omit or use 'index' to list all skills.\",\n required: false,\n type: String,\n },\n },\n service: async ({ alias: inputAlias }: { alias?: string }) => {\n const alias = normalizeAlias(inputAlias || \"index\");\n\n if (!isValidAlias(alias)) {\n throw new Error(\n `Invalid skill alias \"${alias}\". Use alphanumeric characters, hyphens, and underscores only.`,\n );\n }\n\n if (alias === \"index\") {\n // Get index content\n const indexRecord = await skillStore.get(\"index\");\n const indexContent = indexRecord?.content || \"\";\n\n // List all skills except index\n const allSkills = await skillStore.list();\n const skills = allSkills.filter((s) => s.alias !== \"index\");\n const skillList = skills.map(formatSkillListItem).join(\"\\n\");\n\n if (indexContent) {\n return `${indexContent}\\n\\n## Available Skills\\n\\n${skillList}`;\n }\n return `# Jaypie Skills\\n\\n## Available Skills\\n\\n${skillList}`;\n }\n\n const skill = await skillStore.get(alias);\n if (!skill) {\n throw new Error(\n `Skill \"${alias}\" not found. Use skill(\"index\") to list available skills.`,\n );\n }\n\n // Return raw file content for non-index skills (preserve frontmatter)\n const skillPath = path.join(SKILLS_PATH, `${alias}.md`);\n return fs.readFile(skillPath, \"utf-8\");\n },\n});\n\n// =============================================================================\n// VERSION SERVICE\n// =============================================================================\n\nexport const versionService = fabricService({\n alias: \"version\",\n description: `Prints the current version and hash, \\`${BUILD_VERSION_STRING}\\``,\n input: {},\n service: async () => BUILD_VERSION_STRING,\n});\n\n// =============================================================================\n// RELEASE NOTES SERVICE\n// =============================================================================\n\nasync function getReleaseNotesHelp(): Promise<string> {\n return fs.readFile(path.join(__dirname, \"release-notes\", \"help.md\"), \"utf-8\");\n}\n\ninterface ReleaseNotesInput {\n package?: string;\n since_version?: string;\n version?: string;\n}\n\nexport const releaseNotesService = fabricService({\n alias: \"release_notes\",\n description:\n \"Browse Jaypie package release notes. Commands: list, read. Call with no args for help.\",\n input: {\n command: {\n description: \"Command to execute (omit for help)\",\n required: false,\n type: String,\n },\n input: {\n description: \"Command parameters\",\n required: false,\n type: Object,\n },\n },\n service: async ({\n command,\n input: params,\n }: {\n command?: string;\n input?: ReleaseNotesInput;\n }) => {\n if (!command || command === \"help\") {\n return getReleaseNotesHelp();\n }\n\n const p = params || {};\n\n switch (command) {\n case \"list\": {\n const entries = await fs.readdir(RELEASE_NOTES_PATH, {\n withFileTypes: true,\n });\n const packageDirs = entries\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name);\n const packagesToList = p.package\n ? packageDirs.filter((pkg) => pkg === p.package)\n : packageDirs;\n\n if (packagesToList.length === 0 && p.package) {\n return `No release notes found for package \"${p.package}\".`;\n }\n\n const allNotes = await Promise.all(\n packagesToList.map((pkg) => getPackageReleaseNotes(pkg)),\n );\n let flatNotes = allNotes.flat();\n\n if (p.since_version) {\n flatNotes = filterReleaseNotesSince(flatNotes, p.since_version);\n }\n\n if (flatNotes.length === 0) {\n const filterDesc = p.since_version\n ? ` newer than ${p.since_version}`\n : \"\";\n return `No release notes found${filterDesc}.`;\n }\n\n return flatNotes.map(formatReleaseNoteListItem).join(\"\\n\");\n }\n\n case \"read\": {\n if (!p.package) throw new Error(\"package is required\");\n if (!p.version) throw new Error(\"version is required\");\n const filePath = path.join(\n RELEASE_NOTES_PATH,\n p.package,\n `${p.version}.md`,\n );\n return fs.readFile(filePath, \"utf-8\");\n }\n\n default:\n throw new Error(\n `Unknown command: ${command}. Use release_notes() for help.`,\n );\n }\n },\n});\n"],"names":["__filename","__dirname"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/suites/docs/index.ts"],"sourcesContent":["/**\n * Docs Suite - Documentation services (skill, version, release_notes)\n */\nimport { fabricService } from \"@jaypie/fabric\";\nimport {\n createMarkdownStore,\n isValidAlias,\n normalizeAlias,\n type SkillRecord,\n} from \"@jaypie/tildeskill\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport matter from \"gray-matter\";\nimport { gt } from \"semver\";\n\n// Build-time constants\ndeclare const __BUILD_VERSION_STRING__: string;\nconst BUILD_VERSION_STRING =\n typeof __BUILD_VERSION_STRING__ !== \"undefined\"\n ? __BUILD_VERSION_STRING__\n : \"@jaypie/mcp@0.0.0\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n// From dist/suites/docs/, go up 3 levels to package root where skills/ and release-notes/ live\nconst RELEASE_NOTES_PATH = path.join(\n __dirname,\n \"..\",\n \"..\",\n \"..\",\n \"release-notes\",\n);\nconst SKILLS_PATH = path.join(__dirname, \"..\", \"..\", \"..\", \"skills\");\n\n// Create skill store using tildeskill\nconst skillStore = createMarkdownStore({ path: SKILLS_PATH });\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\ninterface ReleaseNoteFrontMatter {\n date?: string;\n summary?: string;\n version?: string;\n}\n\nasync function parseReleaseNoteFile(filePath: string): Promise<{\n date?: string;\n filename: string;\n summary?: string;\n version?: string;\n}> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n const filename = path.basename(filePath, \".md\");\n\n if (content.startsWith(\"---\")) {\n const parsed = matter(content);\n const frontMatter = parsed.data as ReleaseNoteFrontMatter;\n return {\n date: frontMatter.date,\n filename,\n summary: frontMatter.summary,\n version: frontMatter.version || filename,\n };\n }\n\n return { filename, version: filename };\n } catch {\n return { filename: path.basename(filePath, \".md\") };\n }\n}\n\nfunction formatReleaseNoteListItem(note: {\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}): string {\n const { date, packageName, summary, version } = note;\n const parts = [`* ${packageName}@${version}`];\n\n if (date) {\n parts.push(`(${date})`);\n }\n\n if (summary) {\n parts.push(`- ${summary}`);\n }\n\n return parts.join(\" \");\n}\n\nfunction formatSkillListItem(skill: SkillRecord): string {\n const { alias, description } = skill;\n if (description) {\n return `* ${alias} - ${description}`;\n }\n return `* ${alias}`;\n}\n\nasync function getPackageReleaseNotes(packageName: string): Promise<\n Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>\n> {\n const packageDir = path.join(RELEASE_NOTES_PATH, packageName);\n try {\n const files = await fs.readdir(packageDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const notes = await Promise.all(\n mdFiles.map(async (file) => {\n const parsed = await parseReleaseNoteFile(path.join(packageDir, file));\n return { ...parsed, packageName };\n }),\n );\n\n return notes.sort((a, b) => {\n if (!a.version || !b.version) return 0;\n try {\n return gt(a.version, b.version) ? -1 : 1;\n } catch {\n return b.version.localeCompare(a.version);\n }\n });\n } catch {\n return [];\n }\n}\n\nfunction filterReleaseNotesSince(\n notes: Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>,\n sinceVersion: string,\n): Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}> {\n return notes.filter((note) => {\n if (!note.version) return false;\n try {\n return gt(note.version, sinceVersion);\n } catch {\n return false;\n }\n });\n}\n\n// =============================================================================\n// SKILL SERVICE\n// =============================================================================\n\n/**\n * Generate alternative spellings for plural/singular matching\n */\nfunction getAlternativeSpellings(alias: string): string[] {\n const alternatives: string[] = [];\n\n if (alias.endsWith(\"es\")) {\n // \"indexes\" -> try \"indexe\" and \"index\"\n alternatives.push(alias.slice(0, -1)); // Remove \"s\" -> \"indexe\"\n alternatives.push(alias.slice(0, -2)); // Remove \"es\" -> \"index\"\n } else if (alias.endsWith(\"s\")) {\n // \"skills\" -> try \"skill\"\n alternatives.push(alias.slice(0, -1)); // Remove \"s\" -> \"skill\"\n } else {\n // \"fish\" -> try \"fishs\" and \"fishes\"\n alternatives.push(alias + \"s\");\n alternatives.push(alias + \"es\");\n }\n\n return alternatives;\n}\n\n/**\n * Add alias to frontmatter indicating the canonical skill name\n */\nfunction addAliasToFrontmatter(content: string, matchedAlias: string): string {\n if (content.startsWith(\"---\")) {\n // Find the end of frontmatter\n const endIndex = content.indexOf(\"---\", 3);\n if (endIndex !== -1) {\n // Insert alias before the closing ---\n const beforeClose = content.slice(0, endIndex);\n const afterClose = content.slice(endIndex);\n return `${beforeClose}alias: ${matchedAlias}\\n${afterClose}`;\n }\n }\n // No frontmatter exists, create one\n return `---\\nalias: ${matchedAlias}\\n---\\n\\n${content}`;\n}\n\nexport const skillService = fabricService({\n alias: \"skill\",\n description:\n \"Access Jaypie development documentation. Pass a skill alias (e.g., 'aws', 'tests', 'errors') to get that documentation. Pass 'index' or no argument to list all available skills.\",\n input: {\n alias: {\n description:\n \"Skill alias (e.g., 'aws', 'tests'). Omit or use 'index' to list all skills.\",\n required: false,\n type: String,\n },\n },\n service: async ({ alias: inputAlias }: { alias?: string }) => {\n const alias = normalizeAlias(inputAlias || \"index\");\n\n if (!isValidAlias(alias)) {\n throw new Error(\n `Invalid skill alias \"${alias}\". Use alphanumeric characters, hyphens, and underscores only.`,\n );\n }\n\n if (alias === \"index\") {\n // Get index content\n const indexRecord = await skillStore.get(\"index\");\n const indexContent = indexRecord?.content || \"\";\n\n // List all skills except index\n const allSkills = await skillStore.list();\n const skills = allSkills.filter((s) => s.alias !== \"index\");\n const skillList = skills.map(formatSkillListItem).join(\"\\n\");\n\n if (indexContent) {\n return `${indexContent}\\n\\n## Available Skills\\n\\n${skillList}`;\n }\n return `# Jaypie Skills\\n\\n## Available Skills\\n\\n${skillList}`;\n }\n\n // Try exact match first\n let skill = await skillStore.get(alias);\n let matchedAlias = alias;\n\n // If no exact match, try alternative spellings (plural/singular)\n if (!skill) {\n const alternatives = getAlternativeSpellings(alias);\n for (const alt of alternatives) {\n skill = await skillStore.get(alt);\n if (skill) {\n matchedAlias = alt;\n break;\n }\n }\n }\n\n if (!skill) {\n throw new Error(\n `Skill \"${alias}\" not found. Use skill(\"index\") to list available skills.`,\n );\n }\n\n // Return raw file content for non-index skills (preserve frontmatter)\n const skillPath = path.join(SKILLS_PATH, `${matchedAlias}.md`);\n let content = await fs.readFile(skillPath, \"utf-8\");\n\n // If we matched via alternative spelling, add alias to indicate canonical name\n if (matchedAlias !== alias) {\n content = addAliasToFrontmatter(content, matchedAlias);\n }\n\n return content;\n },\n});\n\n// =============================================================================\n// VERSION SERVICE\n// =============================================================================\n\nexport const versionService = fabricService({\n alias: \"version\",\n description: `Prints the current version and hash, \\`${BUILD_VERSION_STRING}\\``,\n input: {},\n service: async () => BUILD_VERSION_STRING,\n});\n\n// =============================================================================\n// RELEASE NOTES SERVICE\n// =============================================================================\n\nasync function getReleaseNotesHelp(): Promise<string> {\n return fs.readFile(path.join(__dirname, \"release-notes\", \"help.md\"), \"utf-8\");\n}\n\ninterface ReleaseNotesInput {\n package?: string;\n since_version?: string;\n version?: string;\n}\n\nexport const releaseNotesService = fabricService({\n alias: \"release_notes\",\n description:\n \"Browse Jaypie package release notes. Commands: list, read. Call with no args for help.\",\n input: {\n command: {\n description: \"Command to execute (omit for help)\",\n required: false,\n type: String,\n },\n input: {\n description: \"Command parameters\",\n required: false,\n type: Object,\n },\n },\n service: async ({\n command,\n input: params,\n }: {\n command?: string;\n input?: ReleaseNotesInput;\n }) => {\n if (!command || command === \"help\") {\n return getReleaseNotesHelp();\n }\n\n const p = params || {};\n\n switch (command) {\n case \"list\": {\n const entries = await fs.readdir(RELEASE_NOTES_PATH, {\n withFileTypes: true,\n });\n const packageDirs = entries\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name);\n const packagesToList = p.package\n ? packageDirs.filter((pkg) => pkg === p.package)\n : packageDirs;\n\n if (packagesToList.length === 0 && p.package) {\n return `No release notes found for package \"${p.package}\".`;\n }\n\n const allNotes = await Promise.all(\n packagesToList.map((pkg) => getPackageReleaseNotes(pkg)),\n );\n let flatNotes = allNotes.flat();\n\n if (p.since_version) {\n flatNotes = filterReleaseNotesSince(flatNotes, p.since_version);\n }\n\n if (flatNotes.length === 0) {\n const filterDesc = p.since_version\n ? ` newer than ${p.since_version}`\n : \"\";\n return `No release notes found${filterDesc}.`;\n }\n\n return flatNotes.map(formatReleaseNoteListItem).join(\"\\n\");\n }\n\n case \"read\": {\n if (!p.package) throw new Error(\"package is required\");\n if (!p.version) throw new Error(\"version is required\");\n const filePath = path.join(\n RELEASE_NOTES_PATH,\n p.package,\n `${p.version}.md`,\n );\n return fs.readFile(filePath, \"utf-8\");\n }\n\n default:\n throw new Error(\n `Unknown command: ${command}. Use release_notes() for help.`,\n );\n }\n },\n});\n"],"names":["__filename","__dirname"],"mappings":"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"}
@@ -6,8 +6,20 @@ Debug and inspect LLM provider responses. Useful for understanding how providers
6
6
 
7
7
  | Command | Description | Required Parameters |
8
8
  |---------|-------------|---------------------|
9
+ | `validate` | Validate LLM setup (check API keys) | - |
9
10
  | `debug_call` | Make a debug call and inspect response | `provider`, `message` |
10
11
 
12
+ ## Parameters
13
+
14
+ All parameters are passed at the top level (flat structure):
15
+
16
+ | Parameter | Type | Description |
17
+ |-----------|------|-------------|
18
+ | `command` | string | Command to execute (omit for help) |
19
+ | `provider` | string | LLM provider: anthropic, openai, google, openrouter |
20
+ | `message` | string | Message to send to the LLM provider |
21
+ | `model` | string | Model to use (provider-specific, e.g., gpt-4, claude-3-sonnet) |
22
+
11
23
  ## Providers
12
24
 
13
25
  Supported providers: `openai`, `anthropic`, `gemini`, `openrouter`
@@ -18,14 +30,20 @@ Supported providers: `openai`, `anthropic`, `gemini`, `openrouter`
18
30
  |----------|-------------|
19
31
  | `OPENAI_API_KEY` | OpenAI API key |
20
32
  | `ANTHROPIC_API_KEY` | Anthropic API key |
21
- | `GOOGLE_API_KEY` | Google/Gemini API key |
33
+ | `GOOGLE_API_KEY` or `GEMINI_API_KEY` | Google/Gemini API key |
22
34
  | `OPENROUTER_API_KEY` | OpenRouter API key |
23
35
 
24
36
  ## Examples
25
37
 
26
38
  ```
27
- llm("debug_call", { provider: "openai", message: "Hello, world!" })
28
- llm("debug_call", { provider: "openai", model: "o3-mini", message: "What is 15 * 17? Think step by step." })
39
+ # Debug OpenAI call
40
+ llm({ command: "debug_call", provider: "openai", message: "Hello, world!" })
41
+
42
+ # Debug with specific model
43
+ llm({ command: "debug_call", provider: "openai", model: "o3-mini", message: "What is 15 * 17? Think step by step." })
44
+
45
+ # Debug Anthropic call
46
+ llm({ command: "debug_call", provider: "anthropic", message: "Explain quantum computing" })
29
47
  ```
30
48
 
31
49
  ## Response Fields
@@ -1,11 +1,2 @@
1
- import { type LlmProvider } from "./llm.js";
2
- interface LlmInput {
3
- message?: string;
4
- model?: string;
5
- provider?: LlmProvider;
6
- }
7
- export declare const llmService: import("@jaypie/fabric").Service<{
8
- command?: string;
9
- input?: LlmInput;
10
- }, string | import("./llm.js").LlmDebugCallResult, string | import("./llm.js").LlmDebugCallResult>;
1
+ export declare const llmService: import("@jaypie/fabric").Service<Record<string, unknown>, string | import("./llm.js").LlmDebugCallResult | import("./llm.js").LlmValidationResult, string | import("./llm.js").LlmDebugCallResult | import("./llm.js").LlmValidationResult>;
11
2
  export * from "./llm.js";
@@ -2,7 +2,7 @@ import { fabricService } from '@jaypie/fabric';
2
2
  import * as fs from 'node:fs/promises';
3
3
  import * as path from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
- import { debugLlmCall } from './llm.js';
5
+ import { debugLlmCall, validateLlmSetup } from './llm.js';
6
6
 
7
7
  /**
8
8
  * LLM Suite - Unified LLM debugging and inspection
@@ -25,18 +25,32 @@ const llmService = fabricService({
25
25
  required: false,
26
26
  type: String,
27
27
  },
28
- input: {
29
- description: "Command parameters",
28
+ message: {
29
+ description: "Message to send to the LLM provider",
30
30
  required: false,
31
- type: Object,
31
+ type: String,
32
+ },
33
+ model: {
34
+ description: "Model to use (provider-specific, e.g., gpt-4, claude-3-sonnet)",
35
+ required: false,
36
+ type: String,
37
+ },
38
+ provider: {
39
+ description: "LLM provider: anthropic, openai, google, openrouter",
40
+ required: false,
41
+ type: String,
32
42
  },
33
43
  },
34
- service: async ({ command, input: params, }) => {
44
+ service: async (params) => {
45
+ const { command } = params;
35
46
  if (!command || command === "help") {
36
47
  return getHelp();
37
48
  }
38
- const p = params || {};
49
+ const p = params;
39
50
  switch (command) {
51
+ case "validate": {
52
+ return validateLlmSetup();
53
+ }
40
54
  case "debug_call": {
41
55
  if (!p.provider)
42
56
  throw new Error("provider is required");
@@ -57,5 +71,5 @@ const llmService = fabricService({
57
71
  },
58
72
  });
59
73
 
60
- export { debugLlmCall, llmService };
74
+ export { debugLlmCall, llmService, validateLlmSetup };
61
75
  //# sourceMappingURL=index.js.map