@aibuilders/mcp-coach-server 1.0.8 → 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 +215 -23
  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",
@@ -333,6 +422,89 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
333
422
  ]
334
423
  };
335
424
  }
425
+ case "get_base_url": {
426
+ const forceRefresh = args?.force_refresh === true;
427
+ let baseUrl = DEFAULT_API_BASE_URL;
428
+ let source = "default";
429
+ let cache = null;
430
+ try {
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
+ };
440
+ }
441
+ catch (error) {
442
+ console.error("Failed to fetch OpenAPI specification for base URL; using default:", error);
443
+ }
444
+ // For OpenAI SDK, baseURL should include /v1 since SDK appends paths directly
445
+ const sdkBaseUrl = getSdkBaseUrl(baseUrl);
446
+ return {
447
+ content: [
448
+ {
449
+ type: "text",
450
+ text: JSON.stringify({
451
+ base_url: baseUrl,
452
+ sdk_base_url: sdkBaseUrl,
453
+ source,
454
+ cache,
455
+ prompt_for_ai: `The base URL for AI Builders Space API is: ${baseUrl}
456
+
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.
458
+
459
+ For example:
460
+ - Full endpoint URL: ${baseUrl}/v1/chat/completions
461
+ - Full endpoint URL: ${baseUrl}/v1/deployments
462
+
463
+ If you are using the OpenAI SDK (recommended), use this base URL: ${sdkBaseUrl}
464
+
465
+ The base URL is also documented in the deployment guide, but this tool provides a direct way to retrieve it without parsing the full deployment guide.`,
466
+ usage_examples: {
467
+ direct_http: {
468
+ description: "Direct HTTP requests",
469
+ base_url: baseUrl,
470
+ example: `const response = await fetch('${baseUrl}/v1/chat/completions', {
471
+ method: 'POST',
472
+ headers: {
473
+ 'Authorization': \`Bearer \${process.env.AI_BUILDER_TOKEN}\`,
474
+ 'Content-Type': 'application/json'
475
+ },
476
+ body: JSON.stringify({ model: 'grok-4-fast', messages: [...] })
477
+ });`
478
+ },
479
+ openai_sdk: {
480
+ description: "OpenAI SDK (recommended)",
481
+ base_url: sdkBaseUrl,
482
+ example_node: `import OpenAI from 'openai';
483
+
484
+ const openai = new OpenAI({
485
+ baseURL: '${sdkBaseUrl}',
486
+ apiKey: process.env.AI_BUILDER_TOKEN,
487
+ });`,
488
+ example_python: `from openai import OpenAI
489
+ import os
490
+
491
+ client = OpenAI(
492
+ base_url='${sdkBaseUrl}',
493
+ api_key=os.getenv('AI_BUILDER_TOKEN')
494
+ )`
495
+ }
496
+ },
497
+ important_notes: [
498
+ "Always use this base URL when making API calls to AI Builders Space",
499
+ "The base URL includes the /backend path",
500
+ "For OpenAI SDK compatibility, use the sdk_base_url which includes /v1",
501
+ "All API calls require authentication via AI_BUILDER_TOKEN in the Authorization header"
502
+ ]
503
+ }, null, 2)
504
+ }
505
+ ]
506
+ };
507
+ }
336
508
  default:
337
509
  throw new Error(`Unknown tool: ${name}`);
338
510
  }
@@ -361,7 +533,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
361
533
  description: "Retrieve the OpenAPI specification with endpoint details",
362
534
  inputSchema: {
363
535
  type: "object",
364
- 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
+ }
365
543
  }
366
544
  },
367
545
  {
@@ -399,6 +577,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
399
577
  }
400
578
  }
401
579
  }
580
+ },
581
+ {
582
+ name: "get_base_url",
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.",
584
+ inputSchema: {
585
+ type: "object",
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
+ }
593
+ }
402
594
  }
403
595
  ]
404
596
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aibuilders/mcp-coach-server",
3
- "version": "1.0.8",
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",