@aibuilders/mcp-coach-server 1.0.9 → 1.0.10

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 (2) hide show
  1. package/dist/index.js +141 -45
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16,6 +16,46 @@ const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"),
16
16
  const PACKAGE_VERSION = packageJson.version;
17
17
  const CACHE_DIR = join(homedir(), ".ai-builders-mcp-cache");
18
18
  const DEPLOYMENT_GUIDE_CACHE = join(CACHE_DIR, "deployment_guide_cache.json");
19
+ const OPENAPI_SPEC_CACHE = join(CACHE_DIR, "openapi_spec_cache.json");
20
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
21
+ const API_ORIGIN = "https://space.ai-builders.com";
22
+ const DEFAULT_API_BASE_URL = `${API_ORIGIN}/backend`;
23
+ const OPENAPI_SPEC_URL = `${DEFAULT_API_BASE_URL}/openapi.json`;
24
+ const DEPLOYMENT_GUIDE_URL = `${API_ORIGIN}/deployment-prompt.md`;
25
+ function isCacheFresh(cachedAt) {
26
+ if (!cachedAt) {
27
+ return false;
28
+ }
29
+ const cachedTime = new Date(cachedAt);
30
+ if (Number.isNaN(cachedTime.getTime())) {
31
+ return false;
32
+ }
33
+ return Date.now() - cachedTime.getTime() < CACHE_TTL_MS;
34
+ }
35
+ function isMissingFileError(error) {
36
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
37
+ }
38
+ function deriveBaseUrl(openapiSpec) {
39
+ let baseUrl = DEFAULT_API_BASE_URL;
40
+ try {
41
+ if (openapiSpec?.servers?.length) {
42
+ const url = openapiSpec.servers[0].url;
43
+ if (typeof url === "string" && url.startsWith("http")) {
44
+ baseUrl = url;
45
+ }
46
+ else if (typeof url === "string") {
47
+ baseUrl = `${API_ORIGIN}${url}`;
48
+ }
49
+ }
50
+ }
51
+ catch (error) {
52
+ console.error("Failed to derive base URL from OpenAPI spec:", error);
53
+ }
54
+ return baseUrl;
55
+ }
56
+ function getSdkBaseUrl(baseUrl) {
57
+ return baseUrl.endsWith('/v1') ? baseUrl : `${baseUrl}/v1`;
58
+ }
19
59
  async function ensureCacheDir() {
20
60
  try {
21
61
  await access(CACHE_DIR);
@@ -65,14 +105,17 @@ async function getCachedDeploymentGuide() {
65
105
  const cacheData = JSON.parse(cacheContent);
66
106
  const cachedTime = new Date(cacheData.cached_at);
67
107
  const now = new Date();
68
- if (now.getTime() - cachedTime.getTime() < 24 * 60 * 60 * 1000) {
108
+ if (now.getTime() - cachedTime.getTime() < CACHE_TTL_MS) {
69
109
  return cacheData;
70
110
  }
71
111
  }
72
- catch {
112
+ catch (error) {
113
+ if (!isMissingFileError(error)) {
114
+ console.error("Deployment guide cache read failed:", error);
115
+ }
73
116
  }
74
117
  try {
75
- const response = await fetch("https://space.ai-builders.com/deployment-prompt.md");
118
+ const response = await fetch(DEPLOYMENT_GUIDE_URL);
76
119
  if (response.ok) {
77
120
  const content = await response.text();
78
121
  const cacheData = {
@@ -98,6 +141,60 @@ async function getCachedDeploymentGuide() {
98
141
  source: "default"
99
142
  };
100
143
  }
144
+ async function getCachedOpenApiSpec(forceRefresh = false) {
145
+ await ensureCacheDir();
146
+ let cachedSpec = null;
147
+ try {
148
+ const cacheContent = await readFile(OPENAPI_SPEC_CACHE, "utf-8");
149
+ cachedSpec = JSON.parse(cacheContent);
150
+ if (cachedSpec && !forceRefresh && isCacheFresh(cachedSpec.cached_at)) {
151
+ return {
152
+ spec: cachedSpec.spec,
153
+ cached_at: cachedSpec.cached_at,
154
+ source: "cache"
155
+ };
156
+ }
157
+ }
158
+ catch (error) {
159
+ if (!isMissingFileError(error)) {
160
+ console.error("OpenAPI cache read failed:", error);
161
+ }
162
+ }
163
+ try {
164
+ const response = await fetch(OPENAPI_SPEC_URL);
165
+ if (!response.ok) {
166
+ throw new Error(`Failed to fetch OpenAPI specification: HTTP ${response.status}`);
167
+ }
168
+ const spec = await response.json();
169
+ const cacheData = {
170
+ spec,
171
+ cached_at: new Date().toISOString(),
172
+ url: OPENAPI_SPEC_URL
173
+ };
174
+ try {
175
+ await writeFile(OPENAPI_SPEC_CACHE, JSON.stringify(cacheData, null, 2));
176
+ }
177
+ catch (error) {
178
+ console.error("OpenAPI cache write failed:", error);
179
+ }
180
+ return {
181
+ spec,
182
+ cached_at: cacheData.cached_at,
183
+ source: "remote"
184
+ };
185
+ }
186
+ catch (error) {
187
+ if (cachedSpec?.spec) {
188
+ console.error("Failed to fetch OpenAPI specification; using stale cache:", error);
189
+ return {
190
+ spec: cachedSpec.spec,
191
+ cached_at: cachedSpec.cached_at,
192
+ source: "stale_cache"
193
+ };
194
+ }
195
+ throw error;
196
+ }
197
+ }
101
198
  const server = new Server({
102
199
  name: "ai-builder-mcp",
103
200
  version: PACKAGE_VERSION,
@@ -111,32 +208,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
111
208
  try {
112
209
  switch (name) {
113
210
  case "get_api_specification": {
114
- const response = await fetch("https://space.ai-builders.com/backend/openapi.json");
115
- if (!response.ok) {
116
- throw new Error(`Failed to fetch OpenAPI specification: HTTP ${response.status}`);
117
- }
118
- const openapiSpec = await response.json();
119
- let baseUrl = "https://space.ai-builders.com/backend";
120
- try {
121
- if (openapiSpec?.servers?.length) {
122
- const url = openapiSpec.servers[0].url;
123
- if (url.startsWith("http")) {
124
- baseUrl = url;
125
- }
126
- else {
127
- baseUrl = `https://space.ai-builders.com${url}`;
128
- }
129
- }
130
- }
131
- catch { }
211
+ const forceRefresh = args?.force_refresh === true;
212
+ const cachedOpenApi = await getCachedOpenApiSpec(forceRefresh);
213
+ const openapiSpec = cachedOpenApi.spec;
214
+ const baseUrl = deriveBaseUrl(openapiSpec);
132
215
  // For OpenAI SDK, baseURL should include /v1 since SDK appends paths directly
133
- const sdkBaseUrl = baseUrl.endsWith('/v1') ? baseUrl : `${baseUrl}/v1`;
216
+ const sdkBaseUrl = getSdkBaseUrl(baseUrl);
134
217
  return {
135
218
  content: [
136
219
  {
137
220
  type: "text",
138
221
  text: JSON.stringify({
139
222
  openapi_spec: openapiSpec,
223
+ cache: {
224
+ source: cachedOpenApi.source,
225
+ cached_at: cachedOpenApi.cached_at,
226
+ ttl_hours: 24,
227
+ openapi_spec_url: OPENAPI_SPEC_URL
228
+ },
140
229
  endpoint_info: {
141
230
  base_url: baseUrl,
142
231
  description: "Base URL for the AI Builders API",
@@ -334,32 +423,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
334
423
  };
335
424
  }
336
425
  case "get_base_url": {
337
- let baseUrl = "https://space.ai-builders.com/backend";
426
+ const forceRefresh = args?.force_refresh === true;
427
+ let baseUrl = DEFAULT_API_BASE_URL;
338
428
  let source = "default";
429
+ let cache = null;
339
430
  try {
340
- const response = await fetch("https://space.ai-builders.com/backend/openapi.json");
341
- if (response.ok) {
342
- const openapiSpec = await response.json();
343
- try {
344
- if (openapiSpec?.servers?.length) {
345
- const url = openapiSpec.servers[0].url;
346
- if (url.startsWith("http")) {
347
- baseUrl = url;
348
- }
349
- else {
350
- baseUrl = `https://space.ai-builders.com${url}`;
351
- }
352
- source = "openapi_spec";
353
- }
354
- }
355
- catch { }
356
- }
431
+ const cachedOpenApi = await getCachedOpenApiSpec(forceRefresh);
432
+ baseUrl = deriveBaseUrl(cachedOpenApi.spec);
433
+ source = "openapi_spec";
434
+ cache = {
435
+ source: cachedOpenApi.source,
436
+ cached_at: cachedOpenApi.cached_at,
437
+ ttl_hours: 24,
438
+ openapi_spec_url: OPENAPI_SPEC_URL
439
+ };
357
440
  }
358
441
  catch (error) {
359
- // If fetch fails, use default base URL
442
+ console.error("Failed to fetch OpenAPI specification for base URL; using default:", error);
360
443
  }
361
444
  // For OpenAI SDK, baseURL should include /v1 since SDK appends paths directly
362
- const sdkBaseUrl = baseUrl.endsWith('/v1') ? baseUrl : `${baseUrl}/v1`;
445
+ const sdkBaseUrl = getSdkBaseUrl(baseUrl);
363
446
  return {
364
447
  content: [
365
448
  {
@@ -367,7 +450,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
367
450
  text: JSON.stringify({
368
451
  base_url: baseUrl,
369
452
  sdk_base_url: sdkBaseUrl,
370
- source: source,
453
+ source,
454
+ cache,
371
455
  prompt_for_ai: `The base URL for AI Builders Space API is: ${baseUrl}
372
456
 
373
457
  This is the base URL you should use for all API calls to the AI Builders Space platform. When making HTTP requests, prepend this base URL to the API endpoint paths.
@@ -449,7 +533,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
449
533
  description: "Retrieve the OpenAPI specification with endpoint details",
450
534
  inputSchema: {
451
535
  type: "object",
452
- properties: {}
536
+ properties: {
537
+ force_refresh: {
538
+ type: "boolean",
539
+ description: "Bypass the 24-hour OpenAPI cache and fetch the latest specification",
540
+ default: false
541
+ }
542
+ }
453
543
  }
454
544
  },
455
545
  {
@@ -493,7 +583,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
493
583
  description: "Get the base URL for AI Builders Space API. This tool provides a direct way to retrieve the base URL without parsing the deployment guide.",
494
584
  inputSchema: {
495
585
  type: "object",
496
- properties: {}
586
+ properties: {
587
+ force_refresh: {
588
+ type: "boolean",
589
+ description: "Bypass the 24-hour OpenAPI cache and fetch the latest specification",
590
+ default: false
591
+ }
592
+ }
497
593
  }
498
594
  }
499
595
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aibuilders/mcp-coach-server",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "AI Builder MCP server for AI Builders platform",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",