@vertesia/tools-sdk 1.0.0-dev.20260203.130115Z → 1.0.0-dev.20260225.024852Z

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 (153) hide show
  1. package/lib/cjs/ContentTypesCollection.js +43 -0
  2. package/lib/cjs/ContentTypesCollection.js.map +1 -0
  3. package/lib/cjs/InteractionCollection.js +43 -0
  4. package/lib/cjs/InteractionCollection.js.map +1 -0
  5. package/lib/cjs/SkillCollection.js +384 -0
  6. package/lib/cjs/SkillCollection.js.map +1 -0
  7. package/lib/cjs/ToolCollection.js +221 -0
  8. package/lib/cjs/ToolCollection.js.map +1 -0
  9. package/lib/cjs/ToolRegistry.js +95 -0
  10. package/lib/cjs/ToolRegistry.js.map +1 -0
  11. package/lib/cjs/auth.js +104 -0
  12. package/lib/cjs/auth.js.map +1 -0
  13. package/lib/cjs/build/validate.js +7 -0
  14. package/lib/cjs/build/validate.js.map +1 -0
  15. package/lib/cjs/copy-assets.js +84 -0
  16. package/lib/cjs/copy-assets.js.map +1 -0
  17. package/lib/cjs/index.js +32 -0
  18. package/lib/cjs/index.js.map +1 -0
  19. package/lib/cjs/package.json +3 -0
  20. package/lib/cjs/server/app-package.js +130 -0
  21. package/lib/cjs/server/app-package.js.map +1 -0
  22. package/lib/cjs/server/content-types.js +73 -0
  23. package/lib/cjs/server/content-types.js.map +1 -0
  24. package/lib/cjs/server/interactions.js +89 -0
  25. package/lib/cjs/server/interactions.js.map +1 -0
  26. package/lib/cjs/server/mcp.js +45 -0
  27. package/lib/cjs/server/mcp.js.map +1 -0
  28. package/lib/cjs/server/site.js +36 -0
  29. package/lib/cjs/server/site.js.map +1 -0
  30. package/lib/cjs/server/skills.js +114 -0
  31. package/lib/cjs/server/skills.js.map +1 -0
  32. package/lib/cjs/server/tools.js +87 -0
  33. package/lib/cjs/server/tools.js.map +1 -0
  34. package/lib/cjs/server/types.js +3 -0
  35. package/lib/cjs/server/types.js.map +1 -0
  36. package/lib/cjs/server/widgets.js +27 -0
  37. package/lib/cjs/server/widgets.js.map +1 -0
  38. package/lib/cjs/server.js +136 -0
  39. package/lib/cjs/server.js.map +1 -0
  40. package/lib/cjs/site/styles.js +692 -0
  41. package/lib/cjs/site/styles.js.map +1 -0
  42. package/lib/cjs/site/templates.js +1014 -0
  43. package/lib/cjs/site/templates.js.map +1 -0
  44. package/lib/cjs/types.js +3 -0
  45. package/lib/cjs/types.js.map +1 -0
  46. package/lib/cjs/utils.js +44 -0
  47. package/lib/cjs/utils.js.map +1 -0
  48. package/lib/esm/ContentTypesCollection.js +39 -0
  49. package/lib/esm/ContentTypesCollection.js.map +1 -0
  50. package/lib/esm/InteractionCollection.js +39 -0
  51. package/lib/esm/InteractionCollection.js.map +1 -0
  52. package/lib/esm/SkillCollection.js +377 -0
  53. package/lib/esm/SkillCollection.js.map +1 -0
  54. package/lib/esm/ToolCollection.js +183 -0
  55. package/lib/esm/ToolCollection.js.map +1 -0
  56. package/lib/esm/ToolRegistry.js +90 -0
  57. package/lib/esm/ToolRegistry.js.map +1 -0
  58. package/lib/esm/auth.js +97 -0
  59. package/lib/esm/auth.js.map +1 -0
  60. package/lib/esm/build/validate.js +4 -0
  61. package/lib/esm/build/validate.js.map +1 -0
  62. package/lib/esm/copy-assets.js +81 -0
  63. package/lib/esm/copy-assets.js.map +1 -0
  64. package/lib/esm/index.js +12 -0
  65. package/lib/esm/index.js.map +1 -0
  66. package/lib/esm/server/app-package.js +127 -0
  67. package/lib/esm/server/app-package.js.map +1 -0
  68. package/lib/esm/server/content-types.js +70 -0
  69. package/lib/esm/server/content-types.js.map +1 -0
  70. package/lib/esm/server/interactions.js +86 -0
  71. package/lib/esm/server/interactions.js.map +1 -0
  72. package/lib/esm/server/mcp.js +42 -0
  73. package/lib/esm/server/mcp.js.map +1 -0
  74. package/lib/esm/server/site.js +33 -0
  75. package/lib/esm/server/site.js.map +1 -0
  76. package/lib/esm/server/skills.js +111 -0
  77. package/lib/esm/server/skills.js.map +1 -0
  78. package/lib/esm/server/tools.js +84 -0
  79. package/lib/esm/server/tools.js.map +1 -0
  80. package/lib/esm/server/types.js +2 -0
  81. package/lib/esm/server/types.js.map +1 -0
  82. package/lib/esm/server/widgets.js +24 -0
  83. package/lib/esm/server/widgets.js.map +1 -0
  84. package/lib/esm/server.js +132 -0
  85. package/lib/esm/server.js.map +1 -0
  86. package/lib/esm/site/styles.js +689 -0
  87. package/lib/esm/site/styles.js.map +1 -0
  88. package/lib/esm/site/templates.js +1001 -0
  89. package/lib/esm/site/templates.js.map +1 -0
  90. package/lib/esm/types.js +2 -0
  91. package/lib/esm/types.js.map +1 -0
  92. package/lib/esm/utils.js +38 -0
  93. package/lib/esm/utils.js.map +1 -0
  94. package/lib/types/ContentTypesCollection.d.ts +18 -0
  95. package/lib/types/ContentTypesCollection.d.ts.map +1 -0
  96. package/lib/types/InteractionCollection.d.ts +18 -0
  97. package/lib/types/InteractionCollection.d.ts.map +1 -0
  98. package/lib/types/SkillCollection.d.ts +119 -0
  99. package/lib/types/SkillCollection.d.ts.map +1 -0
  100. package/lib/types/ToolCollection.d.ts +67 -0
  101. package/lib/types/ToolCollection.d.ts.map +1 -0
  102. package/lib/types/ToolRegistry.d.ts +22 -0
  103. package/lib/types/ToolRegistry.d.ts.map +1 -0
  104. package/lib/types/auth.d.ts +32 -0
  105. package/lib/types/auth.d.ts.map +1 -0
  106. package/lib/types/build/validate.d.ts +2 -0
  107. package/lib/types/build/validate.d.ts.map +1 -0
  108. package/lib/types/copy-assets.d.ts +14 -0
  109. package/lib/types/copy-assets.d.ts.map +1 -0
  110. package/lib/types/index.d.ts +12 -0
  111. package/lib/types/index.d.ts.map +1 -0
  112. package/lib/types/server/app-package.d.ts +4 -0
  113. package/lib/types/server/app-package.d.ts.map +1 -0
  114. package/lib/types/server/content-types.d.ts +4 -0
  115. package/lib/types/server/content-types.d.ts.map +1 -0
  116. package/lib/types/server/interactions.d.ts +4 -0
  117. package/lib/types/server/interactions.d.ts.map +1 -0
  118. package/lib/types/server/mcp.d.ts +4 -0
  119. package/lib/types/server/mcp.d.ts.map +1 -0
  120. package/lib/types/server/site.d.ts +4 -0
  121. package/lib/types/server/site.d.ts.map +1 -0
  122. package/lib/types/server/skills.d.ts +4 -0
  123. package/lib/types/server/skills.d.ts.map +1 -0
  124. package/lib/types/server/tools.d.ts +4 -0
  125. package/lib/types/server/tools.d.ts.map +1 -0
  126. package/lib/types/server/types.d.ts +88 -0
  127. package/lib/types/server/types.d.ts.map +1 -0
  128. package/lib/types/server/widgets.d.ts +4 -0
  129. package/lib/types/server/widgets.d.ts.map +1 -0
  130. package/lib/types/server.d.ts +27 -0
  131. package/lib/types/server.d.ts.map +1 -0
  132. package/lib/types/site/styles.d.ts +5 -0
  133. package/lib/types/site/styles.d.ts.map +1 -0
  134. package/lib/types/site/templates.d.ts +60 -0
  135. package/lib/types/site/templates.d.ts.map +1 -0
  136. package/lib/types/types.d.ts +293 -0
  137. package/lib/types/types.d.ts.map +1 -0
  138. package/lib/types/utils.d.ts +10 -0
  139. package/lib/types/utils.d.ts.map +1 -0
  140. package/package.json +4 -4
  141. package/src/ContentTypesCollection.ts +8 -8
  142. package/src/SkillCollection.ts +11 -2
  143. package/src/ToolCollection.ts +9 -18
  144. package/src/ToolRegistry.ts +17 -56
  145. package/src/server/app-package.ts +88 -43
  146. package/src/server/{conyent-types.ts → content-types.ts} +29 -11
  147. package/src/server/interactions.ts +11 -6
  148. package/src/server/tools.ts +3 -22
  149. package/src/server/types.ts +9 -1
  150. package/src/server/widgets.ts +2 -6
  151. package/src/server.ts +1 -1
  152. package/src/types.ts +34 -1
  153. package/src/utils.ts +15 -1
@@ -1,22 +1,7 @@
1
1
  import { AgentToolDefinition } from "@vertesia/common";
2
2
  import { HTTPException } from "hono/http-exception";
3
- import { Tool, ToolExecutionContext, ToolExecutionPayload, ToolExecutionResult } from "./types.js";
3
+ import { Tool, ToolExecutionContext, ToolExecutionPayload, ToolExecutionResult, ToolUseContext } from "./types.js";
4
4
 
5
- /**
6
- * Options for filtering tool definitions
7
- */
8
- export interface ToolFilterOptions {
9
- /**
10
- * If true, only return tools that are available by default (default !== false).
11
- * If false or undefined, return all tools.
12
- */
13
- defaultOnly?: boolean;
14
- /**
15
- * List of tool names that are unlocked (available even if default: false).
16
- * These tools will be included even when defaultOnly is true.
17
- */
18
- unlockedTools?: string[];
19
- }
20
5
 
21
6
  export class ToolRegistry {
22
7
 
@@ -36,46 +21,22 @@ export class ToolRegistry {
36
21
  * @param options - Filtering options
37
22
  * @returns Filtered tool definitions
38
23
  */
39
- getDefinitions(options?: ToolFilterOptions): AgentToolDefinition[] {
40
- const { defaultOnly, unlockedTools = [] } = options || {};
41
- const unlockedSet = new Set(unlockedTools);
42
-
43
- return Object.values(this.registry)
44
- .filter(tool => {
45
- // If not filtering by default, include all tools
46
- if (!defaultOnly) return true;
47
-
48
- // Include if tool is default (default !== false) or is in unlocked list
49
- const isDefault = tool.default !== false;
50
- const isUnlocked = unlockedSet.has(tool.name);
51
- return isDefault || isUnlocked;
52
- })
53
- .map(tool => ({
54
- name: tool.name,
55
- description: tool.description,
56
- input_schema: tool.input_schema,
57
- category: this.category,
58
- default: tool.default,
59
- }));
60
- }
61
-
62
- /**
63
- * Get tools that are in reserve (default: false and not unlocked).
64
- * @param unlockedTools - List of tool names that are unlocked
65
- * @returns Tool definitions for reserve tools
66
- */
67
- getReserveTools(unlockedTools: string[] = []): AgentToolDefinition[] {
68
- const unlockedSet = new Set(unlockedTools);
69
-
70
- return Object.values(this.registry)
71
- .filter(tool => tool.default === false && !unlockedSet.has(tool.name))
72
- .map(tool => ({
73
- name: tool.name,
74
- description: tool.description,
75
- input_schema: tool.input_schema,
76
- category: this.category,
77
- default: tool.default,
78
- }));
24
+ getDefinitions(context?: ToolUseContext): AgentToolDefinition[] {
25
+ const mapTool = (tool: Tool<any>): AgentToolDefinition => ({
26
+ url: `tools/${this.category}`,
27
+ name: tool.name,
28
+ description: tool.description,
29
+ input_schema: tool.input_schema,
30
+ category: this.category,
31
+ default: tool.default,
32
+ });
33
+ let tools = Object.values(this.registry);
34
+ if (context) {
35
+ tools = tools.filter(tool => {
36
+ return tool.isEnabled ? tool.isEnabled(context) : true;
37
+ });
38
+ }
39
+ return tools.map(mapTool);
79
40
  }
80
41
 
81
42
  getTool<ParamsT extends Record<string, any>>(name: string): Tool<ParamsT> {
@@ -1,25 +1,41 @@
1
- import { AppPackage, AppPackageScope, CatalogInteractionRef, InCodeTypeDefinition } from "@vertesia/common";
1
+ import { AppPackage, AppPackageScope, AppWidgetInfo, CatalogInteractionRef, InCodeTypeDefinition } from "@vertesia/common";
2
2
  import { Context, Hono } from "hono";
3
+ import { ToolUseContext } from "../types.js";
3
4
  import { ToolServerConfig } from "./types.js";
4
5
 
6
+ function getRequestPayload<T>(c: Context): Promise<T | undefined> {
7
+ return c.req.method === "POST" ? c.req.json<T>() : Promise.resolve(undefined);
8
+ }
5
9
 
6
- const builders: Record<Exclude<AppPackageScope, 'all'>, (pkg: AppPackage, config: ToolServerConfig, c: Context) => void> = {
7
- tools(pkg: AppPackage, config: ToolServerConfig) {
10
+ const builders: Record<Exclude<AppPackageScope, 'all'>, (pkg: AppPackage, config: ToolServerConfig, c: Context) => Promise<void>> = {
11
+ async tools(pkg: AppPackage, config: ToolServerConfig, c: Context) {
8
12
  const { tools: toolCollections = [], skills: skillCollections = [] } = config;
9
13
 
14
+ const filterContext = await getRequestPayload<ToolUseContext>(c);
15
+
10
16
  // Aggregate all tools from all collections
11
17
  const allTools = toolCollections.flatMap(collection =>
12
- collection.getToolDefinitions()
18
+ collection.getToolDefinitions(filterContext)
13
19
  );
14
20
 
15
21
  // same for skills
16
22
  const allSkills = skillCollections.flatMap(collection =>
17
- collection.getToolDefinitions()
23
+ collection.getToolDefinitions(filterContext)
18
24
  );
19
25
 
20
- pkg.tools = allSkills.concat(allTools);
26
+ // Deduplicate by tool name (skills listed first take priority)
27
+ const seen = new Set<string>();
28
+ const combined = allSkills.concat(allTools);
29
+ pkg.tools = combined.filter(tool => {
30
+ if (seen.has(tool.name)) {
31
+ console.warn(`[app-package] Duplicate tool name "${tool.name}", skipping`);
32
+ return false;
33
+ }
34
+ seen.add(tool.name);
35
+ return true;
36
+ });
21
37
  },
22
- interactions(pkg: AppPackage, config: ToolServerConfig) {
38
+ async interactions(pkg: AppPackage, config: ToolServerConfig) {
23
39
  const allInteractions: CatalogInteractionRef[] = [];
24
40
  for (const coll of (config.interactions || [])) {
25
41
  for (const inter of coll.interactions) {
@@ -35,16 +51,35 @@ const builders: Record<Exclude<AppPackageScope, 'all'>, (pkg: AppPackage, config
35
51
  }
36
52
  pkg.interactions = allInteractions;
37
53
  },
38
- types(pkg: AppPackage, config: ToolServerConfig) {
54
+ async types(pkg: AppPackage, config: ToolServerConfig) {
39
55
  const allTypes: InCodeTypeDefinition[] = [];
40
56
  for (const coll of config.types || []) {
41
57
  for (const type of coll.types) {
42
- allTypes.push(type);
58
+ allTypes.push({
59
+ ...type,
60
+ id: coll.name + ":" + type.name
61
+ });
43
62
  }
44
63
  }
45
64
  pkg.types = allTypes;
46
65
  },
47
- ui(pkg: AppPackage, config: ToolServerConfig, c: Context) {
66
+ async widgets(pkg: AppPackage, config: ToolServerConfig) {
67
+ const { skills: skillCollections = [] } = config;
68
+ const widgets: Record<string, AppWidgetInfo> = {};
69
+ for (const coll of skillCollections) {
70
+ for (const skill of coll.getSkillDefinitions()) {
71
+ if (skill.widgets && skill.widgets.length > 0) {
72
+ widgets[skill.name] = {
73
+ skill: skill.name,
74
+ collection: coll.name,
75
+ url: `/widgets/${skill.widgets[0]}.js`
76
+ } satisfies AppWidgetInfo;
77
+ }
78
+ }
79
+ }
80
+ pkg.widgets = widgets;
81
+ },
82
+ async ui(pkg: AppPackage, config: ToolServerConfig, c: Context) {
48
83
  if (config.uiConfig) {
49
84
  pkg.ui = { ...config.uiConfig };
50
85
  const origin = new URL(c.req.url).origin;
@@ -54,7 +89,7 @@ const builders: Record<Exclude<AppPackageScope, 'all'>, (pkg: AppPackage, config
54
89
  }
55
90
  }
56
91
  },
57
- settings(pkg: AppPackage, config: ToolServerConfig) {
92
+ async settings(pkg: AppPackage, config: ToolServerConfig) {
58
93
  if (config.settings) {
59
94
  pkg.settings_schema = { ...config.settings };
60
95
  }
@@ -62,41 +97,51 @@ const builders: Record<Exclude<AppPackageScope, 'all'>, (pkg: AppPackage, config
62
97
  }
63
98
 
64
99
 
65
- export function createPackageRoute(app: Hono, basePath: string, config: ToolServerConfig) {
66
- const { interactions = [], tools: toolCollections = [], mcpProviders = [] } = config;
67
-
68
- app.get(basePath, (c: Context) => {
69
- const scope = c.req.query('scope') || 'all';
70
- const pkg: AppPackage = {};
71
- interactions; toolCollections; mcpProviders;
100
+ async function handlePackageRequest(c: Context, config: ToolServerConfig) {
101
+ const scope = c.req.query('scope') || 'all';
102
+ const pkg: AppPackage = {};
72
103
 
73
- const scopes = new Set<AppPackageScope>(scope.split(',') as AppPackageScope[]);
74
- // TODO build pkg based on the query param scope
75
- if (scopes.has('all')) {
76
- builders.tools(pkg, config, c);
77
- builders.interactions(pkg, config, c);
78
- builders.types(pkg, config, c);
79
- builders.ui(pkg, config, c);
104
+ const scopes = new Set<AppPackageScope>(scope.split(',') as AppPackageScope[]);
105
+ // TODO build pkg based on the query param scope
106
+ if (scopes.has('all')) {
107
+ await builders.tools(pkg, config, c);
108
+ await builders.interactions(pkg, config, c);
109
+ await builders.types(pkg, config, c);
110
+ await builders.widgets(pkg, config, c);
111
+ await builders.ui(pkg, config, c);
112
+ await builders.settings(pkg, config, c);
113
+ } else {
114
+ if (scopes.has('tools')) {
115
+ await builders.tools(pkg, config, c);
116
+ }
117
+ if (scopes.has('interactions')) {
118
+ await builders.interactions(pkg, config, c);
119
+ }
120
+ if (scopes.has('types')) {
121
+ await builders.types(pkg, config, c);
122
+ }
123
+ if (scopes.has('widgets')) {
124
+ await builders.widgets(pkg, config, c);
125
+ }
126
+ if (scopes.has('ui')) {
127
+ await builders.ui(pkg, config, c);
128
+ }
129
+ if (scopes.has('settings')) {
80
130
  builders.settings(pkg, config, c);
81
- } else {
82
- if (scopes.has('tools')) {
83
- builders.tools(pkg, config, c);
84
- }
85
- if (scopes.has('interactions')) {
86
- builders.interactions(pkg, config, c);
87
- }
88
- if (scopes.has('types')) {
89
- builders.types(pkg, config, c);
90
- }
91
- if (scopes.has('ui')) {
92
- builders.ui(pkg, config, c);
93
- }
94
- if (scopes.has('settings')) {
95
- builders.settings(pkg, config, c);
96
- }
97
131
  }
132
+ }
98
133
 
99
- return c.json(pkg);
100
- });
134
+ return c.json(pkg);
101
135
  }
102
136
 
137
+ export function createPackageRoute(app: Hono, basePath: string, config: ToolServerConfig) {
138
+
139
+ app.get(basePath, (c: Context) => {
140
+ return handlePackageRequest(c, config);
141
+ });
142
+
143
+ app.post(basePath, (c: Context) => {
144
+ return handlePackageRequest(c, config);
145
+ });
146
+
147
+ }
@@ -2,9 +2,10 @@
2
2
 
3
3
  import { InCodeTypeDefinition } from "@vertesia/common";
4
4
  import { Context, Hono } from "hono";
5
+ import { HTTPException } from "hono/http-exception";
5
6
  import { ContentTypesCollection } from "../ContentTypesCollection.js";
6
7
  import { ToolServerConfig } from "./types.js";
7
- import { HTTPException } from "hono/http-exception";
8
+ import { toPathName } from "../utils.js";
8
9
 
9
10
  export function createContentTypesRoute(app: Hono, basePath: string, config: ToolServerConfig) {
10
11
  const { types = [] } = config;
@@ -15,7 +16,7 @@ export function createContentTypesRoute(app: Hono, basePath: string, config: Too
15
16
 
16
17
  for (const coll of types) {
17
18
  for (const type of coll.types) {
18
- allTypes.push(type);
19
+ allTypes.push({ ...type, id: coll.name + ":" + toPathName(type.name) });
19
20
  }
20
21
  }
21
22
 
@@ -39,17 +40,21 @@ export function createContentTypesRoute(app: Hono, basePath: string, config: Too
39
40
  // GET /api/types/:name - Direct access to content type
40
41
  app.get(`${basePath}/:name`, async (c) => {
41
42
  const name = c.req.param('name');
42
-
43
- // Search across all collections for the interaction
44
- for (const coll of types) {
45
- const inter = coll.getTypeByName(name);
46
- if (inter) {
47
- return c.json(inter);
48
- }
43
+ const parts = name.split(':');
44
+ if (parts.length !== 2) {
45
+ throw new HTTPException(400, {
46
+ message: "Invalid content type name. Expected format 'collection:type'"
47
+ });
48
+ }
49
+ const collName = parts[0];
50
+ const typeName = toPathName(parts[1]);
51
+ const ctype = types.find(t => t.name === collName)?.getTypeByName(typeName);
52
+ if (ctype) {
53
+ return c.json({ ...ctype, id: collName + ":" + typeName });
49
54
  }
50
55
 
51
56
  throw new HTTPException(404, {
52
- message: "No interaction found with name: " + name
57
+ message: "No content type found with name: " + name
53
58
  });
54
59
  });
55
60
 
@@ -62,9 +67,22 @@ function createContentTypeEndpoints(coll: ContentTypesCollection): Hono {
62
67
  const endpoint = new Hono();
63
68
 
64
69
  endpoint.get('/', (c: Context) => {
65
- return c.json(coll.types);
70
+ return c.json(coll.types.map(t => ({ ...t, id: coll.name + ":" + toPathName(t.name) })));
66
71
  });
67
72
 
73
+ endpoint.get('/:name', (c: Context) => {
74
+ const name = c.req.param('name');
75
+ const ctype = coll.types.find(t => toPathName(t.name) === name);
76
+ if (!ctype) {
77
+ throw new HTTPException(404, {
78
+ message: "No content type found with name: " + name
79
+ });
80
+ }
81
+ return c.json({
82
+ ...ctype,
83
+ id: coll.name + ":" + toPathName(ctype.name)
84
+ });
85
+ });
68
86
 
69
87
 
70
88
  return endpoint;
@@ -49,12 +49,17 @@ export function createInteractionsRoute(app: Hono, basePath: string, config: Too
49
49
  await authorize(c);
50
50
  const name = c.req.param('name');
51
51
 
52
- // Search across all collections for the interaction
53
- for (const coll of interactions) {
54
- const inter = coll.getInteractionByName(name);
55
- if (inter) {
56
- return c.json(inter);
57
- }
52
+ const parts = name.split(':');
53
+ if (parts.length !== 2) {
54
+ throw new HTTPException(400, {
55
+ message: "Invalid interaction name. Expected format 'collection:interaction'"
56
+ });
57
+ }
58
+ const collName = parts[0];
59
+ const interName = parts[1];
60
+ const inter = interactions.find(t => t.name === collName)?.getInteractionByName(interName);
61
+ if (inter) {
62
+ return c.json({ ...inter, id: collName + ":" + interName });
58
63
  }
59
64
 
60
65
  throw new HTTPException(404, {
@@ -22,20 +22,11 @@ export function createToolsRoute(app: Hono, basePath: string, config: ToolServer
22
22
 
23
23
  app.get(basePath, (c) => {
24
24
  const url = new URL(c.req.url);
25
- const defaultOnly = c.req.query('defaultOnly') === 'true';
26
- const unlockedParam = c.req.query('unlocked');
27
- const unlockedTools = unlockedParam ? unlockedParam.split(',').map(t => t.trim()).filter(Boolean) : [];
28
-
29
- const filterOptions = defaultOnly ? { defaultOnly, unlockedTools } : undefined;
30
25
 
31
26
  const allTools: ToolDefinition[] = [];
32
- let reserveToolCount = 0;
33
27
 
34
28
  for (const coll of tools) {
35
- allTools.push(...coll.getToolDefinitions(filterOptions));
36
- if (defaultOnly) {
37
- reserveToolCount += coll.getReserveTools(unlockedTools).length;
38
- }
29
+ allTools.push(...coll.getToolDefinitions());
39
30
  }
40
31
 
41
32
  return c.json({
@@ -43,7 +34,6 @@ export function createToolsRoute(app: Hono, basePath: string, config: ToolServer
43
34
  title: 'All Tools',
44
35
  description: 'All available tools across all collections',
45
36
  tools: allTools,
46
- reserveToolCount: defaultOnly ? reserveToolCount : undefined,
47
37
  collections: tools.map(t => ({
48
38
  name: t.name,
49
39
  title: t.title,
@@ -97,27 +87,18 @@ function createToolEndpoints(coll: ToolCollection): Hono {
97
87
  // - unlocked=tool1,tool2: Comma-separated list of unlocked tool names
98
88
  endpoint.get('/', (c) => {
99
89
  const importSourceUrl = c.req.query('import') != null;
100
- const defaultOnly = c.req.query('defaultOnly') === 'true';
101
- const unlockedParam = c.req.query('unlocked');
102
- const unlockedTools = unlockedParam ? unlockedParam.split(',').map(t => t.trim()).filter(Boolean) : [];
103
90
 
104
- const filterOptions = defaultOnly ? { defaultOnly, unlockedTools } : undefined;
105
91
  const url = new URL(c.req.url);
106
92
 
107
- const response: ToolCollectionDefinition & { reserveToolCount?: number } = {
93
+ const response: ToolCollectionDefinition = {
108
94
  src: importSourceUrl
109
95
  ? `${url.origin}/libs/vertesia-tools-${coll.name}.js`
110
96
  : `${url.origin}${url.pathname}`,
111
97
  title: coll.title || coll.name,
112
98
  description: coll.description || '',
113
- tools: coll.getToolDefinitions(filterOptions)
99
+ tools: coll.getToolDefinitions()
114
100
  };
115
101
 
116
- // Include reserve count when filtering
117
- if (defaultOnly) {
118
- response.reserveToolCount = coll.getReserveTools(unlockedTools).length;
119
- }
120
-
121
102
  return c.json(response);
122
103
  });
123
104
 
@@ -4,7 +4,7 @@ import { SkillCollection } from "../SkillCollection.js";
4
4
  import { ToolCollection } from "../ToolCollection.js";
5
5
  import { ToolExecutionPayload } from "../types.js";
6
6
  import { JSONSchema } from "@llumiverse/common";
7
- import { AppUIConfig } from "@vertesia/common";
7
+ import { AppUIConfig, ProjectConfiguration } from "@vertesia/common";
8
8
  import { ContentTypesCollection } from "../ContentTypesCollection.js";
9
9
 
10
10
  /**
@@ -83,5 +83,13 @@ export interface ToolServerConfig {
83
83
  * Hide UI app links on the index page (default: false)
84
84
  */
85
85
  hideUILinks?: boolean;
86
+
87
+ /**
88
+ * If a filter is provided, it will be called with the project configuration when requesting tool definitions, and can be used to filter which tools are returned based on the project configuration.
89
+ * This allows for dynamic enabling/disabling of tools based on project settings.
90
+ * @param projectConfig
91
+ * @returns
92
+ */
93
+ toolFilter?: (projectConfig: ProjectConfiguration) => boolean;
86
94
  }
87
95
 
@@ -1,11 +1,7 @@
1
+ import { AppWidgetInfo } from "@vertesia/common";
1
2
  import { Hono } from "hono";
2
3
  import { ToolServerConfig } from "./types.js";
3
4
 
4
- export interface WidgetInfo {
5
- collection: string;
6
- skill: string;
7
- url: string;
8
- }
9
5
 
10
6
  export function createWidgetsRoute(app: Hono, basePath: string, config: ToolServerConfig) {
11
7
 
@@ -15,7 +11,7 @@ export function createWidgetsRoute(app: Hono, basePath: string, config: ToolServ
15
11
  app.get(basePath, (c) => {
16
12
  const url = new URL(c.req.url);
17
13
 
18
- const widgets: Record<string, WidgetInfo> = {};
14
+ const widgets: Record<string, AppWidgetInfo> = {};
19
15
  for (const coll of skills) {
20
16
  const collWidgets = coll.getWidgets();
21
17
  for (const widget of collWidgets) {
package/src/server.ts CHANGED
@@ -11,7 +11,7 @@ import { ToolContext, ToolServerConfig } from "./server/types.js";
11
11
  import { ToolExecutionPayload } from "./types.js";
12
12
  import { createWidgetsRoute } from "./server/widgets.js";
13
13
  import { createPackageRoute } from "./server/app-package.js";
14
- import { createContentTypesRoute } from "./server/conyent-types.js";
14
+ import { createContentTypesRoute } from "./server/content-types.js";
15
15
 
16
16
  // Schema for tool execution payload
17
17
  const ToolExecutionPayloadSchema = z.object({
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ToolDefinition, ToolUse } from "@llumiverse/common";
2
2
  import { VertesiaClient } from "@vertesia/client";
3
- import { AgentToolDefinition, AuthTokenPayload, ToolExecutionMetadata, ToolResult, ToolResultContent } from "@vertesia/common";
3
+ import { AgentToolDefinition, AuthTokenPayload, ProjectConfiguration, ToolExecutionMetadata, ToolResult, ToolResultContent } from "@vertesia/common";
4
4
 
5
5
  export type { ToolExecutionMetadata };
6
6
 
@@ -87,6 +87,15 @@ export interface ToolExecutionPayload<ParamsT extends Record<string, any>> {
87
87
 
88
88
  export type ToolFn<ParamsT extends Record<string, any>> = (payload: ToolExecutionPayload<ParamsT>, context: ToolExecutionContext) => Promise<ToolExecutionResult>;
89
89
 
90
+ export interface ToolUseContext {
91
+ project_id?: string,
92
+ account_id?: string,
93
+ project_name?: string,
94
+ project_ns?: string,
95
+ configuration?: ProjectConfiguration;
96
+ vars?: Record<string, any>;
97
+ }
98
+
90
99
  export interface Tool<ParamsT extends Record<string, any>> extends ToolDefinition {
91
100
  run: ToolFn<ParamsT>;
92
101
  /**
@@ -95,6 +104,15 @@ export interface Tool<ParamsT extends Record<string, any>> extends ToolDefinitio
95
104
  * - false: Tool is only available when activated by a skill's related_tools
96
105
  */
97
106
  default?: boolean;
107
+
108
+ /**
109
+ * Optional filter to check if the tool is enabled for the given project configuration.
110
+ * This can be used to dynamically enable/disable tools based on project settings, environment variables, or any other logic.
111
+ * If no filter is provided, the tool will be enabled by default.
112
+ * @param payload
113
+ * @returns
114
+ */
115
+ isEnabled?: (payload: ToolUseContext) => boolean;
98
116
  }
99
117
 
100
118
 
@@ -127,6 +145,11 @@ export interface MCPConnectionDetails {
127
145
  * If an empty string no authentication will be done
128
146
  */
129
147
  token: string;
148
+ /**
149
+ * Optional additional HTTP headers to include with requests to the MCP server.
150
+ * Merged with the Authorization header derived from the token.
151
+ */
152
+ headers?: Record<string, string>;
130
153
  }
131
154
 
132
155
  // ================== Skill Types ==================
@@ -234,6 +257,16 @@ export interface SkillDefinition {
234
257
  * The widget file must be located in the skill directory under the name {{widget-name}}.tsx.
235
258
  */
236
259
  widgets?: string[];
260
+
261
+ /**
262
+ * Optional filter to check if the tool is enabled for the given project configuration.
263
+ * This can be used to dynamically enable/disable tools based on project settings, environment variables, or any other logic.
264
+ * If no filter is provided, the tool will be enabled by default.
265
+ * @param payload
266
+ * @returns
267
+ */
268
+ isEnabled?: (payload: ToolUseContext) => boolean;
269
+
237
270
  }
238
271
 
239
272
  /**
package/src/utils.ts CHANGED
@@ -1,3 +1,16 @@
1
+ /**
2
+ * Convert a name to a URL-safe path segment.
3
+ * If the name contains spaces or underscores, convert to PascalCase.
4
+ * Otherwise return as-is. Any characters not in [a-zA-Z0-9$_\-@] are replaced with '-'.
5
+ */
6
+ export function toPathName(name: string): string {
7
+ let result = name;
8
+ if (result.includes(' ') || result.includes('_')) {
9
+ result = result.split(/[\s_]+/).map(w => w.charAt(0).toUpperCase() + w.slice(1)).join('');
10
+ }
11
+ return result.replace(/[^a-zA-Z0-9$_\-@]/g, '-');
12
+ }
13
+
1
14
  export function kebabCaseToTitle(name: string) {
2
15
  return name.split('-').map(p => p[0].toUpperCase() + p.substring(1)).join(' ');
3
16
  }
@@ -20,4 +33,5 @@ export function join(left: string, right: string) {
20
33
  } else {
21
34
  return left + '/' + right;
22
35
  }
23
- }
36
+ }
37
+