opentool 0.8.6 → 0.8.8

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,6 @@
1
1
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
- import { I as InternalToolDefinition, T as ToolResponse } from './validate-D5sE9GUm.js';
3
- export { B as BuildConfig, m as BuildMetadata, H as HTTP_METHODS, f as HttpHandlerDefinition, e as HttpMethod, h as McpConfig, M as Metadata, N as NormalizedSchedule, P as PaymentConfig, S as ScheduleType, i as ServerConfig, j as Tool, d as ToolContent, k as ToolMetadataOverrides, b as generateMetadata, g as generateMetadataCommand, l as loadAndValidateTools, v as validateCommand } from './validate-D5sE9GUm.js';
4
- import { z, ZodSchema } from 'zod';
2
+ import { I as InternalToolDefinition, T as ToolResponse } from './validate-3e5UvzfQ.js';
3
+ export { B as BuildConfig, m as BuildMetadata, C as CronSpec, q as GetHandler, H as HTTP_METHODS, f as HttpHandlerDefinition, e as HttpMethod, h as McpConfig, M as Metadata, N as NormalizedSchedule, P as PaymentConfig, r as PostHandler, S as ScheduleType, i as ServerConfig, j as Tool, n as ToolCategory, d as ToolContent, k as ToolMetadataOverrides, u as ToolModule, s as ToolModuleGET, t as ToolModulePOST, o as ToolProfileGET, p as ToolProfilePOST, b as generateMetadata, g as generateMetadataCommand, l as loadAndValidateTools, v as validateCommand } from './validate-3e5UvzfQ.js';
5
4
  export { CurrencySpec, DEFAULT_FACILITATOR, DefineX402PaymentConfig, EIP3009Authorization, PAYMENT_HEADERS, RequireX402PaymentOptions, RequireX402PaymentOutcome, RequireX402PaymentSuccess, SUPPORTED_CURRENCIES, X402BrowserClient, X402BrowserClientConfig, X402Client, X402ClientConfig, X402FacilitatorConfig, X402PayRequest, X402PayResult, X402Payment, X402PaymentContext, X402PaymentDefinition, X402PaymentRequiredError, X402VerificationResult, defineX402Payment, getX402PaymentContext, payX402, payX402WithWallet, requireX402Payment, withX402Payment } from './x402/index.js';
6
5
  export { DEFAULT_CHAIN, DEFAULT_TOKENS, chains, getRpcUrl, registry, tokens, wallet, walletToolkit } from './wallet/index.js';
7
6
  export { HyperliquidApiError, HyperliquidApproveBuilderFeeOptions, HyperliquidApproveBuilderFeeResponse, HyperliquidBuilderApprovalError, HyperliquidBuilderApprovalRecordInput, HyperliquidBuilderFee, HyperliquidClearinghouseState, HyperliquidDepositResult, HyperliquidEnvironment, HyperliquidExchangeClient, HyperliquidExchangeResponse, HyperliquidGrouping, HyperliquidGuardError, HyperliquidInfoClient, HyperliquidOrderIntent, HyperliquidOrderOptions, HyperliquidOrderResponse, HyperliquidOrderStatus, HyperliquidTermsError, HyperliquidTermsRecordInput, HyperliquidTriggerOptions, HyperliquidTriggerType, HyperliquidWithdrawResult, NonceSource, __hyperliquidInternals, approveHyperliquidBuilderFee, batchModifyHyperliquidOrders, cancelAllHyperliquidOrders, cancelHyperliquidOrders, cancelHyperliquidOrdersByCloid, cancelHyperliquidTwapOrder, createHyperliquidSubAccount, createMonotonicNonceFactory, depositToHyperliquidBridge, fetchHyperliquidAssetCtxs, fetchHyperliquidClearinghouseState, fetchHyperliquidFrontendOpenOrders, fetchHyperliquidHistoricalOrders, fetchHyperliquidMeta, fetchHyperliquidMetaAndAssetCtxs, fetchHyperliquidOpenOrders, fetchHyperliquidOrderStatus, fetchHyperliquidPreTransferCheck, fetchHyperliquidSpotAssetCtxs, fetchHyperliquidSpotClearinghouseState, fetchHyperliquidSpotMeta, fetchHyperliquidSpotMetaAndAssetCtxs, fetchHyperliquidUserFills, fetchHyperliquidUserFillsByTime, fetchHyperliquidUserRateLimit, getHyperliquidMaxBuilderFee, modifyHyperliquidOrder, placeHyperliquidOrder, placeHyperliquidTwapOrder, recordHyperliquidBuilderApproval, recordHyperliquidTermsAcceptance, reserveHyperliquidRequestWeight, scheduleHyperliquidCancel, sendHyperliquidSpot, setHyperliquidPortfolioMargin, transferHyperliquidSubAccount, updateHyperliquidIsolatedMargin, updateHyperliquidLeverage, withdrawFromHyperliquid } from './adapters/hyperliquid/index.js';
@@ -9,6 +8,7 @@ import { c as WalletFullContext } from './types-BVLpaY4O.js';
9
8
  export { C as ChainMetadata, i as ChainReference, a as ChainTokenMap, H as Hex, g as HexAddress, R as RpcProviderOptions, h as RpcUrlResolver, T as TokenMetadata, l as TurnkeyOptions, k as TurnkeySignWith, r as WalletBaseContext, s as WalletContext, n as WalletOptions, m as WalletOptionsBase, b as WalletPrivateKeyOptions, j as WalletProviderType, f as WalletReadonlyContext, e as WalletReadonlyOptions, W as WalletRegistry, o as WalletSendTransactionParams, q as WalletSignerContext, p as WalletTransferParams, d as WalletTurnkeyOptions } from './types-BVLpaY4O.js';
10
9
  export { AIAbortError, AIClientConfig, AIError, AIFetchError, AIRequestMetadata, AIResponseError, ChatCompletionChoice, ChatCompletionLogProbs, ChatCompletionResponse, ChatCompletionUsage, ChatMessage, ChatMessageContentPart, ChatMessageContentPartImageUrl, ChatMessageContentPartText, ChatMessageRole, DEFAULT_BASE_URL, DEFAULT_MODEL, DEFAULT_TIMEOUT_MS, FunctionToolDefinition, GenerateTextOptions, GenerateTextResult, GenerationParameters, JsonSchema, ResolvedAIClientConfig, ResponseErrorDetails, StreamTextOptions, StreamTextResult, StreamingEventHandlers, ToolChoice, ToolDefinition, ToolExecutionPolicy, WEBSEARCH_TOOL_DEFINITION, WEBSEARCH_TOOL_NAME, WebSearchOptions, createAIClient, ensureTextContent, flattenMessageContent, generateText, getModelConfig, isStreamingSupported, isToolCallingSupported, listModels, normalizeModelName, resolveConfig, resolveToolset, streamText } from './ai/index.js';
11
10
  export { AgentDigestRequest, MyToolsResponse, StoreAction, StoreError, StoreEventInput, StoreOptions, StoreResponse, StoreRetrieveParams, StoreRetrieveResult, ToolExecuteRequest, ToolExecuteResponse, executeTool, getMyPerformance, getMyTools, postAgentDigest, retrieve, store } from './store/index.js';
11
+ import { ZodSchema } from 'zod';
12
12
  import 'viem';
13
13
  import 'viem/accounts';
14
14
 
@@ -22,44 +22,6 @@ declare function createDevServer(tools: InternalToolDefinition[]): Server;
22
22
  declare function createStdioServer(tools?: InternalToolDefinition[]): Promise<void>;
23
23
  declare function resolveRuntimePath(value: string): string;
24
24
 
25
- type CronSpec = {
26
- /**
27
- * AWS EventBridge schedule expression (`cron(...)` or `rate(...)`).
28
- */
29
- cron: string;
30
- enabled?: boolean;
31
- notifyEmail?: boolean;
32
- };
33
- type ToolProfileGET = {
34
- description: string;
35
- schedule: CronSpec;
36
- fixedAmount?: string;
37
- tokenSymbol?: string;
38
- limits?: {
39
- concurrency?: number;
40
- dailyCap?: number;
41
- };
42
- };
43
- type ToolProfilePOST = {
44
- description?: string;
45
- notifyEmail?: boolean;
46
- };
47
- type GetHandler = (req: Request) => Promise<Response> | Response;
48
- type PostHandler = (req: Request) => Promise<Response> | Response;
49
- type ToolModuleGET = {
50
- profile: ToolProfileGET;
51
- GET: GetHandler;
52
- POST?: never;
53
- schema?: never;
54
- };
55
- type ToolModulePOST<B = unknown> = {
56
- profile?: ToolProfilePOST;
57
- POST: PostHandler;
58
- schema: z.ZodType<B>;
59
- GET?: never;
60
- };
61
- type ToolModule = ToolModuleGET | ToolModulePOST<any>;
62
-
63
25
  type PolymarketEnvironment = "mainnet" | "testnet";
64
26
  type PolymarketSide = "BUY" | "SELL";
65
27
  type PolymarketOrderType = "GTC" | "FOK" | "FAK" | "GTD";
@@ -386,4 +348,4 @@ interface CreateMcpAdapterOptions {
386
348
  declare function createMcpAdapter(options: CreateMcpAdapterOptions): (rawArguments: unknown) => Promise<ToolResponse>;
387
349
  declare function responseToToolResponse(response: Response): Promise<ToolResponse>;
388
350
 
389
- export { type CronSpec, type GetHandler, InternalToolDefinition, POLYMARKET_CHAIN_ID, POLYMARKET_CLOB_AUTH_DOMAIN, POLYMARKET_CLOB_DOMAIN, POLYMARKET_ENDPOINTS, POLYMARKET_EXCHANGE_ADDRESSES, type PolymarketApiCredentials, PolymarketApiError, type PolymarketApiKeyResponse, PolymarketAuthError, type PolymarketEnvironment, PolymarketExchangeClient, PolymarketInfoClient, type PolymarketMarket, type PolymarketOrderIntent, type PolymarketOrderType, type PolymarketOrderbook, type PolymarketPlaceOrderResponse, type PolymarketPriceHistoryPoint, type PolymarketSide, type PolymarketSignatureType, type PolymarketSignedOrderPayload, type PostHandler, type ToolModule, type ToolModuleGET, type ToolModulePOST, type ToolProfileGET, type ToolProfilePOST, ToolResponse, WalletFullContext, buildHmacSignature, buildL1Headers, buildL2Headers, buildPolymarketOrderAmounts, buildSignedOrderPayload, cancelAllPolymarketOrders, cancelMarketPolymarketOrders, cancelPolymarketOrder, cancelPolymarketOrders, createDevServer, createMcpAdapter, createPolymarketApiKey, createStdioServer, derivePolymarketApiKey, fetchPolymarketMarket, fetchPolymarketMarkets, fetchPolymarketMidpoint, fetchPolymarketOrderbook, fetchPolymarketPrice, fetchPolymarketPriceHistory, normalizeNumberArrayish, normalizeStringArrayish, placePolymarketOrder, resolveExchangeAddress, resolvePolymarketBaseUrl, resolveRuntimePath, responseToToolResponse };
351
+ export { InternalToolDefinition, POLYMARKET_CHAIN_ID, POLYMARKET_CLOB_AUTH_DOMAIN, POLYMARKET_CLOB_DOMAIN, POLYMARKET_ENDPOINTS, POLYMARKET_EXCHANGE_ADDRESSES, type PolymarketApiCredentials, PolymarketApiError, type PolymarketApiKeyResponse, PolymarketAuthError, type PolymarketEnvironment, PolymarketExchangeClient, PolymarketInfoClient, type PolymarketMarket, type PolymarketOrderIntent, type PolymarketOrderType, type PolymarketOrderbook, type PolymarketPlaceOrderResponse, type PolymarketPriceHistoryPoint, type PolymarketSide, type PolymarketSignatureType, type PolymarketSignedOrderPayload, ToolResponse, WalletFullContext, buildHmacSignature, buildL1Headers, buildL2Headers, buildPolymarketOrderAmounts, buildSignedOrderPayload, cancelAllPolymarketOrders, cancelMarketPolymarketOrders, cancelPolymarketOrder, cancelPolymarketOrders, createDevServer, createMcpAdapter, createPolymarketApiKey, createStdioServer, derivePolymarketApiKey, fetchPolymarketMarket, fetchPolymarketMarkets, fetchPolymarketMidpoint, fetchPolymarketOrderbook, fetchPolymarketPrice, fetchPolymarketPriceHistory, normalizeNumberArrayish, normalizeStringArrayish, placePolymarketOrder, resolveExchangeAddress, resolvePolymarketBaseUrl, resolveRuntimePath, responseToToolResponse };
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
+ import * as path6 from 'path';
2
+ import { fileURLToPath, pathToFileURL } from 'url';
1
3
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
4
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
5
  import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
6
  import * as fs2 from 'fs';
5
- import * as path5 from 'path';
6
- import { fileURLToPath, pathToFileURL } from 'url';
7
7
  import { zodToJsonSchema } from '@alcyone-labs/zod-to-json-schema';
8
8
  import { z } from 'zod';
9
9
  import { zeroAddress, createWalletClient, http, createPublicClient, parseUnits, encodeFunctionData, erc20Abi } from 'viem';
@@ -25,6 +25,8 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
25
25
  if (typeof require !== "undefined") return require.apply(this, arguments);
26
26
  throw Error('Dynamic require of "' + x + '" is not supported');
27
27
  });
28
+ var getFilename = () => fileURLToPath(import.meta.url);
29
+ var __filename = /* @__PURE__ */ getFilename();
28
30
  var X402_VERSION = 1;
29
31
  var HEADER_X402 = "X-PAYMENT";
30
32
  var HEADER_PAYMENT_RESPONSE = "X-PAYMENT-RESPONSE";
@@ -988,7 +990,7 @@ function buildAdapters(tools) {
988
990
  }
989
991
  async function loadToolsFromDirectory(metadataMap) {
990
992
  const tools = [];
991
- const toolsDir = path5.join(process.cwd(), "tools");
993
+ const toolsDir = path6.join(process.cwd(), "tools");
992
994
  if (!fs2.existsSync(toolsDir)) {
993
995
  return tools;
994
996
  }
@@ -997,7 +999,7 @@ async function loadToolsFromDirectory(metadataMap) {
997
999
  if (!isSupportedToolFile(file)) {
998
1000
  continue;
999
1001
  }
1000
- const toolPath = path5.join(toolsDir, file);
1002
+ const toolPath = path6.join(toolsDir, file);
1001
1003
  try {
1002
1004
  const exportsObject = __require(toolPath);
1003
1005
  const candidate = resolveModuleCandidate(exportsObject);
@@ -1061,7 +1063,7 @@ async function loadToolsFromDirectory(metadataMap) {
1061
1063
  return tools;
1062
1064
  }
1063
1065
  function loadMetadata() {
1064
- const metadataPath = path5.join(process.cwd(), "metadata.json");
1066
+ const metadataPath = path6.join(process.cwd(), "metadata.json");
1065
1067
  if (!fs2.existsSync(metadataPath)) {
1066
1068
  return null;
1067
1069
  }
@@ -1183,7 +1185,7 @@ function resolveRuntimePath(value) {
1183
1185
  if (value.startsWith("file://")) {
1184
1186
  return fileURLToPath(value);
1185
1187
  }
1186
- return path5.resolve(value);
1188
+ return path6.resolve(value);
1187
1189
  }
1188
1190
 
1189
1191
  // src/types/index.ts
@@ -3358,9 +3360,9 @@ function parseOptionalDate(value) {
3358
3360
  function buildHmacSignature(args) {
3359
3361
  const timestamp2 = args.timestamp.toString();
3360
3362
  const method = args.method.toUpperCase();
3361
- const path7 = args.path;
3363
+ const path8 = args.path;
3362
3364
  const body = args.body == null ? "" : typeof args.body === "string" ? args.body : JSON.stringify(args.body);
3363
- const payload = `${timestamp2}${method}${path7}${body}`;
3365
+ const payload = `${timestamp2}${method}${path8}${body}`;
3364
3366
  const key = Buffer.from(args.secret, "base64");
3365
3367
  return createHmac("sha256", key).update(payload).digest("hex");
3366
3368
  }
@@ -4725,9 +4727,9 @@ function assignIfDefined(target, key, value) {
4725
4727
  target[key] = value;
4726
4728
  }
4727
4729
  }
4728
- function buildUrl(baseUrl, path7) {
4730
+ function buildUrl(baseUrl, path8) {
4729
4731
  const sanitizedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
4730
- return `${sanitizedBase}${path7}`;
4732
+ return `${sanitizedBase}${path8}`;
4731
4733
  }
4732
4734
  function createAbortBundle(upstreamSignal, timeoutMs) {
4733
4735
  const controller = new AbortController();
@@ -4870,6 +4872,7 @@ var DiscoveryMetadataSchema = z.object({
4870
4872
  compatibility: z.record(z.string(), z.any()).optional(),
4871
4873
  documentation: z.union([z.string(), z.array(z.string())]).optional()
4872
4874
  }).catchall(z.any());
4875
+ var ToolCategorySchema = z.enum(["strategy", "tracker"]);
4873
4876
  var ToolMetadataOverridesSchema = z.object({
4874
4877
  name: z.string().optional(),
4875
4878
  description: z.string().optional(),
@@ -4886,7 +4889,8 @@ var ToolSchema = z.object({
4886
4889
  payment: PaymentConfigSchema.optional(),
4887
4890
  discovery: DiscoveryMetadataSchema.optional(),
4888
4891
  chains: z.array(z.union([z.string(), z.number()])).optional(),
4889
- notifyEmail: z.boolean().optional()
4892
+ notifyEmail: z.boolean().optional(),
4893
+ category: ToolCategorySchema.optional()
4890
4894
  }).strict();
4891
4895
  var MetadataSchema = z.object({
4892
4896
  metadataSpecVersion: z.string().optional(),
@@ -4938,7 +4942,7 @@ var BuildMetadataSchema = z.object({
4938
4942
  chains: z.array(z.union([z.string(), z.number()])).optional()
4939
4943
  }).strict();
4940
4944
  function resolveTsconfig(projectRoot) {
4941
- const candidate = path5.join(projectRoot, "tsconfig.json");
4945
+ const candidate = path6.join(projectRoot, "tsconfig.json");
4942
4946
  if (fs2.existsSync(candidate)) {
4943
4947
  return candidate;
4944
4948
  }
@@ -4949,7 +4953,7 @@ async function transpileWithEsbuild(options) {
4949
4953
  throw new Error("No entry points provided for esbuild transpilation");
4950
4954
  }
4951
4955
  const projectRoot = options.projectRoot;
4952
- const tempBase = options.outDir ?? fs2.mkdtempSync(path5.join(tmpdir(), "opentool-"));
4956
+ const tempBase = options.outDir ?? fs2.mkdtempSync(path6.join(tmpdir(), "opentool-"));
4953
4957
  if (!fs2.existsSync(tempBase)) {
4954
4958
  fs2.mkdirSync(tempBase, { recursive: true });
4955
4959
  }
@@ -4993,7 +4997,7 @@ async function transpileWithEsbuild(options) {
4993
4997
  }
4994
4998
  await build(buildOptions);
4995
4999
  if (options.format === "esm") {
4996
- const packageJsonPath = path5.join(tempBase, "package.json");
5000
+ const packageJsonPath = path6.join(tempBase, "package.json");
4997
5001
  if (!fs2.existsSync(packageJsonPath)) {
4998
5002
  fs2.writeFileSync(packageJsonPath, JSON.stringify({ type: "module" }), "utf8");
4999
5003
  }
@@ -5006,10 +5010,12 @@ async function transpileWithEsbuild(options) {
5006
5010
  };
5007
5011
  return { outDir: tempBase, cleanup };
5008
5012
  }
5009
- createRequire(import.meta.url);
5013
+ createRequire(
5014
+ typeof __filename !== "undefined" ? __filename : import.meta.url
5015
+ );
5010
5016
  function resolveCompiledPath(outDir, originalFile, extension = ".js") {
5011
- const baseName = path5.basename(originalFile).replace(/\.[^.]+$/, "");
5012
- return path5.join(outDir, `${baseName}${extension}`);
5017
+ const baseName = path6.basename(originalFile).replace(/\.[^.]+$/, "");
5018
+ return path6.join(outDir, `${baseName}${extension}`);
5013
5019
  }
5014
5020
  async function importFresh(modulePath) {
5015
5021
  const fileUrl = pathToFileURL(modulePath).href;
@@ -5021,13 +5027,13 @@ async function importFresh(modulePath) {
5021
5027
  // src/cli/shared/metadata.ts
5022
5028
  var METADATA_ENTRY = "metadata.ts";
5023
5029
  async function loadMetadata2(projectRoot) {
5024
- const absPath = path5.join(projectRoot, METADATA_ENTRY);
5030
+ const absPath = path6.join(projectRoot, METADATA_ENTRY);
5025
5031
  if (!fs2.existsSync(absPath)) {
5026
5032
  throw new Error(
5027
5033
  `metadata.ts not found in ${projectRoot}. Create metadata.ts to describe your agent.`
5028
5034
  );
5029
5035
  }
5030
- const tempDir = path5.join(projectRoot, ".opentool-temp");
5036
+ const tempDir = path6.join(projectRoot, ".opentool-temp");
5031
5037
  if (fs2.existsSync(tempDir)) {
5032
5038
  fs2.rmSync(tempDir, { recursive: true, force: true });
5033
5039
  }
@@ -5068,7 +5074,7 @@ function extractMetadataExport(moduleExports) {
5068
5074
  return moduleExports;
5069
5075
  }
5070
5076
  function readPackageJson(projectRoot) {
5071
- const packagePath = path5.join(projectRoot, "package.json");
5077
+ const packagePath = path6.join(projectRoot, "package.json");
5072
5078
  if (!fs2.existsSync(packagePath)) {
5073
5079
  return {};
5074
5080
  }
@@ -5084,7 +5090,7 @@ async function buildMetadataArtifact(options) {
5084
5090
  const packageInfo = readPackageJson(projectRoot);
5085
5091
  const { metadata: authored, sourcePath } = await loadMetadata2(projectRoot);
5086
5092
  const defaultsApplied = [];
5087
- const folderName = path5.basename(projectRoot);
5093
+ const folderName = path6.basename(projectRoot);
5088
5094
  const name = resolveField(
5089
5095
  "name",
5090
5096
  authored.name,
@@ -5141,6 +5147,10 @@ async function buildMetadataArtifact(options) {
5141
5147
  }
5142
5148
  const toolDiscovery = overrides.discovery ?? void 0;
5143
5149
  const toolChains = overrides.chains ?? authored.chains ?? void 0;
5150
+ const toolCategory = tool.profileCategory ?? "tracker";
5151
+ if (!tool.profileCategory) {
5152
+ defaultsApplied.push(`tool ${toolName} category \u2192 tracker (default)`);
5153
+ }
5144
5154
  const toolDefinition = {
5145
5155
  name: toolName,
5146
5156
  description: toolDescription,
@@ -5158,10 +5168,14 @@ async function buildMetadataArtifact(options) {
5158
5168
  if (toolChains) {
5159
5169
  toolDefinition.chains = toolChains;
5160
5170
  }
5171
+ toolDefinition.category = toolCategory;
5161
5172
  const notifyEmail = tool.notifyEmail ?? tool.schedule?.notifyEmail;
5162
5173
  if (notifyEmail !== void 0) {
5163
5174
  toolDefinition.notifyEmail = notifyEmail;
5164
5175
  }
5176
+ if (tool.profileCategory) {
5177
+ toolDefinition.category = tool.profileCategory;
5178
+ }
5165
5179
  return toolDefinition;
5166
5180
  });
5167
5181
  const metadata = BuildMetadataSchema.parse({
@@ -5296,11 +5310,11 @@ var SUPPORTED_EXTENSIONS = [
5296
5310
  async function validateCommand(options) {
5297
5311
  console.log("\u{1F50D} Validating OpenTool metadata...");
5298
5312
  try {
5299
- const toolsDir = path5.resolve(options.input);
5313
+ const toolsDir = path6.resolve(options.input);
5300
5314
  if (!fs2.existsSync(toolsDir)) {
5301
5315
  throw new Error(`Tools directory not found: ${toolsDir}`);
5302
5316
  }
5303
- const projectRoot = path5.dirname(toolsDir);
5317
+ const projectRoot = path6.dirname(toolsDir);
5304
5318
  const tools = await loadAndValidateTools(toolsDir, { projectRoot });
5305
5319
  if (tools.length === 0) {
5306
5320
  throw new Error("No valid tools found - metadata validation aborted");
@@ -5317,12 +5331,12 @@ async function validateCommand(options) {
5317
5331
  }
5318
5332
  }
5319
5333
  async function loadAndValidateTools(toolsDir, options = {}) {
5320
- const files = fs2.readdirSync(toolsDir).filter((file) => SUPPORTED_EXTENSIONS.includes(path5.extname(file)));
5334
+ const files = fs2.readdirSync(toolsDir).filter((file) => SUPPORTED_EXTENSIONS.includes(path6.extname(file)));
5321
5335
  if (files.length === 0) {
5322
5336
  return [];
5323
5337
  }
5324
- const projectRoot = options.projectRoot ?? path5.dirname(toolsDir);
5325
- const tempDir = path5.join(toolsDir, ".opentool-temp");
5338
+ const projectRoot = options.projectRoot ?? path6.dirname(toolsDir);
5339
+ const tempDir = path6.join(toolsDir, ".opentool-temp");
5326
5340
  if (fs2.existsSync(tempDir)) {
5327
5341
  fs2.rmSync(tempDir, { recursive: true, force: true });
5328
5342
  }
@@ -5332,7 +5346,7 @@ async function loadAndValidateTools(toolsDir, options = {}) {
5332
5346
  throw new Error(`Tool filename must be kebab-case: ${f}`);
5333
5347
  }
5334
5348
  }
5335
- const entryPoints = files.map((file) => path5.join(toolsDir, file));
5349
+ const entryPoints = files.map((file) => path6.join(toolsDir, file));
5336
5350
  const { outDir, cleanup } = await transpileWithEsbuild({
5337
5351
  entryPoints,
5338
5352
  projectRoot,
@@ -5372,6 +5386,13 @@ async function loadAndValidateTools(toolsDir, options = {}) {
5372
5386
  let normalizedSchedule = null;
5373
5387
  const schedule = toolModule?.profile?.schedule;
5374
5388
  const profileNotifyEmail = typeof toolModule?.profile?.notifyEmail === "boolean" ? toolModule.profile.notifyEmail : void 0;
5389
+ const profileCategoryRaw = typeof toolModule?.profile?.category === "string" ? toolModule.profile.category : void 0;
5390
+ const allowedProfileCategories = /* @__PURE__ */ new Set(["strategy", "tracker"]);
5391
+ if (profileCategoryRaw && !allowedProfileCategories.has(profileCategoryRaw)) {
5392
+ throw new Error(
5393
+ `${file}: profile.category must be one of ${Array.from(allowedProfileCategories).join(", ")}`
5394
+ );
5395
+ }
5375
5396
  if (hasGET && schedule && typeof schedule.cron === "string" && schedule.cron.trim().length > 0) {
5376
5397
  normalizedSchedule = normalizeScheduleExpression(schedule.cron, file);
5377
5398
  if (typeof schedule.enabled === "boolean") {
@@ -5437,12 +5458,13 @@ async function loadAndValidateTools(toolsDir, options = {}) {
5437
5458
  httpHandlers,
5438
5459
  mcpConfig: normalizeMcpConfig(toolModule.mcp, file),
5439
5460
  filename: toBaseName(file),
5440
- sourcePath: path5.join(toolsDir, file),
5461
+ sourcePath: path6.join(toolsDir, file),
5441
5462
  handler: async (params) => adapter(params),
5442
5463
  payment: paymentExport ?? null,
5443
5464
  schedule: normalizedSchedule,
5444
5465
  ...profileNotifyEmail !== void 0 ? { notifyEmail: profileNotifyEmail } : {},
5445
- profileDescription: typeof toolModule?.profile?.description === "string" ? toolModule.profile?.description ?? null : null
5466
+ profileDescription: typeof toolModule?.profile?.description === "string" ? toolModule.profile?.description ?? null : null,
5467
+ ...profileCategoryRaw ? { profileCategory: profileCategoryRaw } : {}
5446
5468
  };
5447
5469
  tools.push(tool);
5448
5470
  }
@@ -5651,17 +5673,17 @@ async function generateMetadataCommand(options) {
5651
5673
  }
5652
5674
  }
5653
5675
  async function generateMetadata(options) {
5654
- const toolsDir = path5.resolve(options.input);
5676
+ const toolsDir = path6.resolve(options.input);
5655
5677
  if (!fs2.existsSync(toolsDir)) {
5656
5678
  throw new Error(`Tools directory not found: ${toolsDir}`);
5657
5679
  }
5658
- const projectRoot = path5.dirname(toolsDir);
5680
+ const projectRoot = path6.dirname(toolsDir);
5659
5681
  const tools = await loadAndValidateTools(toolsDir, { projectRoot });
5660
5682
  const { metadata, defaultsApplied } = await buildMetadataArtifact({
5661
5683
  projectRoot,
5662
5684
  tools
5663
5685
  });
5664
- const outputPath = options.output ? path5.resolve(options.output) : path5.join(projectRoot, "metadata.json");
5686
+ const outputPath = options.output ? path6.resolve(options.output) : path6.join(projectRoot, "metadata.json");
5665
5687
  fs2.writeFileSync(outputPath, JSON.stringify(metadata, null, 2));
5666
5688
  return {
5667
5689
  metadata,