@amitdeshmukh/ax-crew 8.7.3 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { AxCrew } from './agents/index.js';
2
2
  import { AxCrewFunctions } from './functions/index.js';
3
3
  import type { AxCrewConfig, AxCrewOptions, AgentConfig, AgentExecutionMode, AxCrewAxAgentOptions } from './types.js';
4
- import type { UsageCost, AggregatedMetrics, AggregatedCosts, StateInstance, FunctionRegistryType, ACEConfig, ACETeacherConfig, ACEPersistenceConfig, ACEOptionsConfig, ACEMetricConfig } from './types.js';
4
+ import type { UsageCost, AggregatedMetrics, AggregatedCosts, StateInstance, FunctionRegistryType, DeferredToolsConfig, ACEConfig, ACETeacherConfig, ACEPersistenceConfig, ACEOptionsConfig, ACEMetricConfig } from './types.js';
5
5
  /**
6
6
  * Metrics types and helpers for request counts, token usage, and estimated cost.
7
7
  *
@@ -29,4 +29,4 @@ export {
29
29
  /** See class JSDoc on the `AxCrew` implementation. */
30
30
  _AxCrew as AxCrew,
31
31
  /** Built-in function registry; see file docs in `src/functions/index.ts`. */
32
- _AxCrewFunctions as AxCrewFunctions, FunctionRegistryType, type AggregatedMetrics, type AggregatedCosts, type AgentConfig, type AgentExecutionMode, type AxCrewAxAgentOptions, type AxCrewConfig, type AxCrewOptions, type StateInstance, type UsageCost, type ACEConfig, type ACETeacherConfig, type ACEPersistenceConfig, type ACEOptionsConfig, type ACEMetricConfig, };
32
+ _AxCrewFunctions as AxCrewFunctions, FunctionRegistryType, type AggregatedMetrics, type AggregatedCosts, type AgentConfig, type AgentExecutionMode, type AxCrewAxAgentOptions, type AxCrewConfig, type AxCrewOptions, type StateInstance, type UsageCost, type DeferredToolsConfig, type ACEConfig, type ACETeacherConfig, type ACEPersistenceConfig, type ACEOptionsConfig, type ACEMetricConfig, };
package/dist/types.d.ts CHANGED
@@ -173,6 +173,21 @@ interface ACEConfig {
173
173
  metric?: ACEMetricConfig;
174
174
  compileOnStart?: boolean;
175
175
  }
176
+ /**
177
+ * Configuration for deferred tool loading.
178
+ * When an agent has many tools, only core tools are visible by default.
179
+ * A search_tools meta-function lets the LLM discover and activate deferred tools on demand.
180
+ */
181
+ interface DeferredToolsConfig {
182
+ /** Enable deferred tool loading. Default: auto (true when tool count > threshold) */
183
+ enabled?: boolean;
184
+ /** Tool count threshold to activate deferred mode. Default: 20 */
185
+ threshold?: number;
186
+ /** Max tools returned per search. Default: 10 */
187
+ maxSearchResults?: number;
188
+ /** Tool names to always keep active (bypasses deferral) */
189
+ coreTools?: string[];
190
+ }
176
191
  /**
177
192
  * The configuration for an agent.
178
193
  *
@@ -234,6 +249,8 @@ interface AgentConfig {
234
249
  mcpServers?: Record<string, MCPTransportConfig>;
235
250
  /** Optional AxACE configuration to enable optimization for this agent */
236
251
  ace?: ACEConfig;
252
+ /** Deferred tool loading — reduces token usage when an agent has many tools */
253
+ deferredTools?: DeferredToolsConfig;
237
254
  }
238
255
  /**
239
256
  * The configuration object for an AxCrew instance.
@@ -290,4 +307,4 @@ interface AxCrewOptions {
290
307
  meter?: any;
291
308
  };
292
309
  }
293
- export { type AgentConfig, type AxCrewConfig, type AxCrewOptions, type AggregatedMetrics, type StateInstance, type FunctionRegistryType, type MCPStdioTransportConfig, type MCPHTTPSSETransportConfig, type MCPStreamableHTTPTransportConfig, type MCPTransportConfig, type ModelUsage, type ModelInfo, type UsageCost, type AggregatedCosts, type ACEConfig, type ACEMetricConfig, type ACEOptionsConfig, type ACEPersistenceConfig, type ACETeacherConfig };
310
+ export { type AgentConfig, type AxCrewConfig, type AxCrewOptions, type AggregatedMetrics, type StateInstance, type FunctionRegistryType, type MCPStdioTransportConfig, type MCPHTTPSSETransportConfig, type MCPStreamableHTTPTransportConfig, type MCPTransportConfig, type ModelUsage, type ModelInfo, type UsageCost, type AggregatedCosts, type DeferredToolsConfig, type ACEConfig, type ACEMetricConfig, type ACEOptionsConfig, type ACEPersistenceConfig, type ACETeacherConfig };
@@ -5,27 +5,17 @@ import dotenv from "dotenv";
5
5
  dotenv.config();
6
6
 
7
7
  /**
8
- * GraphJin MCP Server Example
8
+ * GraphJin MCP Server Example — with deferred tool loading
9
9
  *
10
- * This example demonstrates how to use GraphJin as an MCP server to give AI agents
11
- * direct access to databases. GraphJin auto-discovers your database schema and provides
12
- * tools for querying, schema exploration, and more.
10
+ * This example demonstrates deferred tool loading: when an agent has many
11
+ * MCP tools, only core tools + a search_tools meta-function are visible
12
+ * to the LLM. The LLM discovers and activates deferred tools on demand.
13
13
  *
14
14
  * Setup:
15
15
  * 1. Install GraphJin: npm install -g graphjin
16
16
  * 2. Start GraphJin demo server: graphjin serve --demo --path /path/to/graphjin/examples/webshop
17
17
  * 3. GraphJin will start on http://localhost:8080
18
18
  * 4. The MCP proxy connects to it via stdio
19
- *
20
- * Alternative setup (direct mode):
21
- * If you have a GraphJin config, you can use direct mode without a running server:
22
- * - Use command: "graphjin" with args: ["mcp", "--demo", "--path", "/path/to/config"]
23
- *
24
- * Note: There is currently a schema validation issue between GraphJin and Ax.
25
- * GraphJin's array parameters don't include "items" definitions in their JSON Schema,
26
- * which Ax requires per JSON Schema spec. A fix is needed in either:
27
- * - GraphJin's mcp-go integration (recommended)
28
- * - Or Ax's schema validation (less recommended)
29
19
  */
30
20
 
31
21
  // Define the crew configuration
@@ -33,50 +23,68 @@ const config = {
33
23
  crew: [
34
24
  {
35
25
  name: "DatabaseAgent",
36
- description: "An agent with direct database access via GraphJin. Can query products, customers, orders, and explore database schema.",
37
- signature: 'dbQuery:string "a database question or query request" -> dbResult:string "the query result or answer"',
38
- provider: "google-gemini",
39
- providerKeyName: "GEMINI_API_KEY",
26
+ description: "An agent with direct database access via GraphJin. Can explore database schema, query tables, list save and run workflows in the builtin JS sandbox etc.",
27
+ definition: `You answer questions by querying databases via a GraphJin server.
28
+ You have resource docs available (query syntax, mutation syntax, workflow guides, JS runtime API). Read them to learn the GraphJin DSL — it differs from standard GraphQL.
29
+ Use search_tools to discover additional action tools not shown by default.
30
+
31
+ STRATEGY:
32
+ 1. Check first — call list_workflows and list_saved_queries. If a match exists, execute it and skip to step 5.
33
+
34
+ 2. Learn the environment:
35
+ a. Read resource docs (get_query_syntax, get_js_runtime_api) to understand the DSL and runtime API.
36
+ b. Call list_tables + describe_table for schema details. Use explore_relationships or find_path if joins are needed.
37
+
38
+ 3. Build and validate:
39
+ a. Author a JavaScript workflow using gj.tools.* for server-side computation. Design it with input variables — never hardcode values.
40
+ b. IMPORTANT: The database enforces a low default row limit (e.g. 20) on ALL queries including aggregations. Your workflow MUST paginate using cursor-based pagination (first: 20, after: cursor) to fetch complete results. Use first: 20 (not higher — the server silently caps it). Loop until the cursor stops changing or returns null.
41
+ c. Call execute_graphql first to validate query shape and results before embedding in a workflow.
42
+
43
+ 4. Save and run — call save_workflow with a descriptive snake_case name and tags, then execute_workflow.
44
+
45
+ 5. If a query fails, do not retry the same query. Call fix_query_error or explain_query to diagnose, then fix and re-save.
46
+
47
+ 6. Synthesize the answer from results.`,
48
+ signature: 'question:string "a natural language question about the database" -> answer:string "the answer to the question"',
49
+ provider: "anthropic",
50
+ providerKeyName: "ANTHROPIC_API_KEY",
40
51
  ai: {
41
- model: "gemini-2.5-pro",
52
+ model: "claude-sonnet-4-6",
42
53
  temperature: 0,
43
54
  stream: false
44
55
  },
45
56
  options: {
46
57
  debug: true
47
58
  },
48
- // MCP Server Configuration for GraphJin
49
- // This assumes you have graphjin running on http://localhost:8080
50
- // Start it with: graphjin serve --demo --path /path/to/graphjin/examples/webshop
51
59
  mcpServers: {
52
60
  "graphjin": {
53
61
  "command": "graphjin",
54
- // Proxy mode: connects to a running GraphJin HTTP server
55
62
  "args": ["mcp", "--server", "http://localhost:8080"]
56
-
57
- // Direct mode (alternative - uncomment to use):
58
- // Runs GraphJin MCP server directly with demo database
59
- // "args": ["mcp", "--demo", "--path", "/path/to/your/graphjin/config"]
60
63
  }
61
64
  },
65
+ deferredTools: {
66
+ enabled: true,
67
+ threshold: 5,
68
+ },
62
69
  },
63
70
  {
64
71
  name: "ManagerAgent",
65
72
  description: "Orchestrates database queries and analysis tasks",
66
- prompt: `You are a manager agent that helps users get insights from databases.
67
- You can delegate to the DatabaseAgent for any database queries or schema exploration.
68
- Keep your responses clear and well-formatted.`,
73
+ definition: `You are an orchestrator that routes questions to specialized agents.
74
+ Delegate each question to the most relevant agent in a SINGLE call — do not break questions into sub-queries.
75
+ The sub-agent will handle all the steps internally. Your job is to route and synthesize, not to decompose.
76
+ If multiple agents are needed, call them and combine their answers.`,
69
77
  signature: 'question:string "a question to be answered" -> answer:string "the answer to the question"',
70
- provider: "google-gemini",
71
- providerKeyName: "GEMINI_API_KEY",
78
+ provider: "anthropic",
79
+ providerKeyName: "ANTHROPIC_API_KEY",
72
80
  ai: {
73
- model: "gemini-2.5-pro",
81
+ model: "claude-sonnet-4-6",
74
82
  maxTokens: 2000,
75
83
  temperature: 0,
76
84
  stream: false
77
85
  },
78
86
  options: {
79
- debug: true,
87
+ debug: false,
80
88
  },
81
89
  agents: ["DatabaseAgent"]
82
90
  }
@@ -86,50 +94,53 @@ Keep your responses clear and well-formatted.`,
86
94
  // Create a new instance of AxCrew with the config
87
95
  const crew = new AxCrew(config as AxCrewConfig);
88
96
 
89
- // Example queries to try:
90
- const queries = [
91
- "What tables are available in the database?",
92
- "Show me the schema for the products table",
93
- "How many products are in the database?",
94
- "List the top 5 most expensive products",
95
- "What customers have placed orders in the last 30 days?"
96
- ];
97
+ const userQuery = "which products had the most refund requests and why?";
97
98
 
98
- // Use a simpler query for testing
99
- const userQuery: string = queries[0]; // "What tables are available in the database?"
100
-
101
- console.log(`\n\nQuestion: ${userQuery}`);
99
+ console.log(`\nQuestion: ${userQuery}`);
102
100
 
103
101
  const main = async (): Promise<void> => {
102
+ const timers: Record<string, number> = {};
103
+
104
104
  try {
105
- // Initialize agents inside main so initialization failures are handled here.
105
+ // --- Setup phase ---
106
+ const t0 = performance.now();
106
107
  await crew.addAllAgents();
108
+ timers["setup"] = performance.now() - t0;
107
109
 
108
110
  const managerAgent = crew.agents?.get("ManagerAgent");
109
- const databaseAgent = crew.agents?.get("DatabaseAgent");
110
111
 
111
112
  if (!managerAgent) {
112
113
  throw new Error("Failed to initialize ManagerAgent");
113
114
  }
114
115
 
116
+ // --- Query phase ---
117
+ console.log("\n--- Starting query ---\n");
118
+ const t1 = performance.now();
119
+
115
120
  const managerResponse = await managerAgent.forward({
116
121
  question: userQuery,
117
122
  });
118
123
 
124
+ timers["query"] = performance.now() - t1;
125
+ timers["total"] = performance.now() - t0;
126
+
127
+ // --- Results ---
119
128
  console.log(`\nAnswer: ${JSON.stringify(managerResponse?.answer, null, 2)}`);
120
129
 
121
- // Print metrics
122
- console.log("\nMetrics:\n+++++++++++++++++++++++++++++++++");
123
- console.log("Manager Agent Metrics:", JSON.stringify((managerAgent as any)?.getMetrics?.(), null, 2));
124
- console.log("Database Agent Metrics:", JSON.stringify((databaseAgent as any)?.getMetrics?.(), null, 2));
125
- console.log("Crew Metrics:", JSON.stringify((crew as any)?.getCrewMetrics?.(), null, 2));
130
+ // --- Timing & Metrics ---
131
+ console.log("\n--- Performance ---");
132
+ console.log(` Setup: ${(timers["setup"]! / 1000).toFixed(2)}s`);
133
+ console.log(` Query: ${(timers["query"]! / 1000).toFixed(2)}s`);
134
+ console.log(` Total: ${(timers["total"]! / 1000).toFixed(2)}s`);
135
+
136
+ console.log("\n--- Metrics ---");
137
+ console.log(JSON.stringify((crew as any)?.getCrewMetrics?.(), null, 2));
126
138
  } catch (error) {
127
- console.error("\n❌ Error:", error);
139
+ console.error("\nError:", error);
128
140
  console.error("\nTroubleshooting:");
129
141
  console.error("1. Make sure GraphJin is running: graphjin serve --demo --path /path/to/graphjin/examples/webshop");
130
142
  console.error("2. Check that GraphJin is accessible at http://localhost:8080");
131
143
  console.error("3. Verify graphjin is installed: which graphjin");
132
- console.error("4. Note: There's a known schema validation issue - see comments in the code");
133
144
  throw error;
134
145
  } finally {
135
146
  crew.destroy();
@@ -138,10 +149,10 @@ const main = async (): Promise<void> => {
138
149
 
139
150
  main()
140
151
  .then(() => {
141
- console.log("\n✅ Done");
152
+ console.log("\nDone");
142
153
  process.exit(0);
143
154
  })
144
155
  .catch((error) => {
145
- console.error("\n❌ Fatal error:", error);
156
+ console.error("\nFatal error:", error);
146
157
  process.exit(1);
147
158
  });
@@ -93,7 +93,7 @@ const main = async () => {
93
93
  // Refer to https://github.com/WP-API/Basic-Auth?tab=readme-ov-file
94
94
 
95
95
  // Set environment variables
96
- crew.state.set("env", {
96
+ crew.crewState.set("env", {
97
97
  WORDPRESS_URL: "http://my-wordpress-site.com",
98
98
  WORDPRESS_USERNAME: "my-username",
99
99
  WORDPRESS_PASSWORD: "my-password"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@amitdeshmukh/ax-crew",
4
- "version": "8.7.3",
4
+ "version": "9.0.0",
5
5
  "description": "Build and launch a crew of AI agents with shared state. Built with axllm.dev",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -54,7 +54,7 @@ const initializeMCPServers = async (agentConfigData: AgentConfig): Promise<AxFun
54
54
 
55
55
  let initializedClients: AxMCPClient[] = [];
56
56
  const functions: AxFunction[] = [];
57
-
57
+
58
58
  try {
59
59
  for (const [mcpServerName, mcpServerConfig] of Object.entries(mcpServers)) {
60
60
  let transport;
@@ -68,7 +68,7 @@ const initializeMCPServers = async (agentConfigData: AgentConfig): Promise<AxFun
68
68
  transport = new AxMCPHTTPSSETransport(mcpServerConfig.sseUrl);
69
69
  } else if (isStreambleHTTPTransport(mcpServerConfig)) {
70
70
  transport = new AxMCPStreambleHTTPTransport(mcpServerConfig.mcpEndpoint, mcpServerConfig.options);
71
- } else {
71
+ } else {
72
72
  throw new Error(`Unsupported transport type: ${JSON.stringify(mcpServerConfig)}`);
73
73
  }
74
74
 
@@ -77,20 +77,21 @@ const initializeMCPServers = async (agentConfigData: AgentConfig): Promise<AxFun
77
77
  initializedClients.push(mcpClient);
78
78
  // Normalize MCP tool schemas: some MCP servers omit `parameters` for
79
79
  // zero-arg tools, but providers like Gemini require a valid schema.
80
- let mcpFns = mcpClient.toFunction().map(fn => ({
80
+ const allFns = mcpClient.toFunction().map(fn => ({
81
81
  ...fn,
82
82
  parameters: fn.parameters ?? { type: 'object' as const, properties: {} },
83
83
  }));
84
84
 
85
- // Filter to allowlisted tools if specified
85
+ // Filter to allowlisted tools if specified, but always include resource_* functions
86
86
  if (mcpServerConfig.tools && mcpServerConfig.tools.length > 0) {
87
87
  const allowed = new Set(mcpServerConfig.tools);
88
- mcpFns = mcpFns.filter(fn => allowed.has(fn.name));
88
+ const filtered = allFns.filter(fn => allowed.has(fn.name) || fn.name.startsWith('resource_'));
89
+ functions.push(...filtered);
90
+ } else {
91
+ functions.push(...allFns);
89
92
  }
90
-
91
- functions.push(...mcpFns);
92
93
  }
93
-
94
+
94
95
  return functions;
95
96
  } catch (error) {
96
97
  initializedClients = [];
@@ -217,6 +218,9 @@ const parseAgentConfig = async (
217
218
  const executionMode: AgentExecutionMode =
218
219
  agentConfigData.executionMode === 'axagent' ? 'axagent' : 'axgen';
219
220
 
221
+ // Track MCP function names for deferred tool loading
222
+ const mcpFunctionNames = new Set(mcpFunctions.map(fn => fn.name));
223
+
220
224
  // Return AI instance and Agent parameters
221
225
  return {
222
226
  ai: aiInstance,
@@ -227,9 +231,11 @@ const parseAgentConfig = async (
227
231
  definition: (agentConfigData as any).definition ?? (agentConfigData as any).prompt,
228
232
  signature: agentConfigData.signature,
229
233
  functions: agentFunctions,
234
+ mcpFunctionNames,
230
235
  subAgentNames: agentConfigData.agents || [],
231
236
  examples: agentConfigData.examples || [],
232
237
  tracker: costTracker,
238
+ deferredTools: (agentConfigData as any).deferredTools,
233
239
  debug: (agentConfigData as any).options?.debug ?? (agentConfigData as any).debug ?? false,
234
240
  };
235
241
  } catch (error) {