@vertesia/tools-sdk 1.0.0-dev.20260128.144200 → 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 (113) 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 +0 -121
  4. package/lib/cjs/InteractionCollection.js.map +1 -1
  5. package/lib/cjs/SkillCollection.js +10 -2
  6. package/lib/cjs/SkillCollection.js.map +1 -1
  7. package/lib/cjs/ToolCollection.js +6 -13
  8. package/lib/cjs/ToolCollection.js.map +1 -1
  9. package/lib/cjs/ToolRegistry.js +16 -32
  10. package/lib/cjs/ToolRegistry.js.map +1 -1
  11. package/lib/cjs/index.js +1 -0
  12. package/lib/cjs/index.js.map +1 -1
  13. package/lib/cjs/server/app-package.js +130 -0
  14. package/lib/cjs/server/app-package.js.map +1 -0
  15. package/lib/cjs/server/content-types.js +73 -0
  16. package/lib/cjs/server/content-types.js.map +1 -0
  17. package/lib/cjs/server/interactions.js +26 -3
  18. package/lib/cjs/server/interactions.js.map +1 -1
  19. package/lib/cjs/server/site.js +7 -1
  20. package/lib/cjs/server/site.js.map +1 -1
  21. package/lib/cjs/server/tools.js +2 -19
  22. package/lib/cjs/server/tools.js.map +1 -1
  23. package/lib/cjs/server/widgets.js.map +1 -1
  24. package/lib/cjs/server.js +4 -0
  25. package/lib/cjs/server.js.map +1 -1
  26. package/lib/cjs/site/styles.js +55 -0
  27. package/lib/cjs/site/styles.js.map +1 -1
  28. package/lib/cjs/site/templates.js +104 -85
  29. package/lib/cjs/site/templates.js.map +1 -1
  30. package/lib/cjs/utils.js +13 -0
  31. package/lib/cjs/utils.js.map +1 -1
  32. package/lib/esm/ContentTypesCollection.js +39 -0
  33. package/lib/esm/ContentTypesCollection.js.map +1 -0
  34. package/lib/esm/InteractionCollection.js +0 -86
  35. package/lib/esm/InteractionCollection.js.map +1 -1
  36. package/lib/esm/SkillCollection.js +10 -2
  37. package/lib/esm/SkillCollection.js.map +1 -1
  38. package/lib/esm/ToolCollection.js +7 -14
  39. package/lib/esm/ToolCollection.js.map +1 -1
  40. package/lib/esm/ToolRegistry.js +16 -32
  41. package/lib/esm/ToolRegistry.js.map +1 -1
  42. package/lib/esm/index.js +1 -0
  43. package/lib/esm/index.js.map +1 -1
  44. package/lib/esm/server/app-package.js +127 -0
  45. package/lib/esm/server/app-package.js.map +1 -0
  46. package/lib/esm/server/content-types.js +70 -0
  47. package/lib/esm/server/content-types.js.map +1 -0
  48. package/lib/esm/server/interactions.js +26 -3
  49. package/lib/esm/server/interactions.js.map +1 -1
  50. package/lib/esm/server/site.js +8 -2
  51. package/lib/esm/server/site.js.map +1 -1
  52. package/lib/esm/server/tools.js +2 -19
  53. package/lib/esm/server/tools.js.map +1 -1
  54. package/lib/esm/server/widgets.js.map +1 -1
  55. package/lib/esm/server.js +4 -0
  56. package/lib/esm/server.js.map +1 -1
  57. package/lib/esm/site/styles.js +55 -0
  58. package/lib/esm/site/styles.js.map +1 -1
  59. package/lib/esm/site/templates.js +103 -85
  60. package/lib/esm/site/templates.js.map +1 -1
  61. package/lib/esm/utils.js +12 -0
  62. package/lib/esm/utils.js.map +1 -1
  63. package/lib/types/ContentTypesCollection.d.ts +18 -0
  64. package/lib/types/ContentTypesCollection.d.ts.map +1 -0
  65. package/lib/types/InteractionCollection.d.ts +1 -31
  66. package/lib/types/InteractionCollection.d.ts.map +1 -1
  67. package/lib/types/SkillCollection.d.ts +3 -2
  68. package/lib/types/SkillCollection.d.ts.map +1 -1
  69. package/lib/types/ToolCollection.d.ts +5 -10
  70. package/lib/types/ToolCollection.d.ts.map +1 -1
  71. package/lib/types/ToolRegistry.d.ts +5 -24
  72. package/lib/types/ToolRegistry.d.ts.map +1 -1
  73. package/lib/types/index.d.ts +1 -0
  74. package/lib/types/index.d.ts.map +1 -1
  75. package/lib/types/server/app-package.d.ts +4 -0
  76. package/lib/types/server/app-package.d.ts.map +1 -0
  77. package/lib/types/server/content-types.d.ts +4 -0
  78. package/lib/types/server/content-types.d.ts.map +1 -0
  79. package/lib/types/server/interactions.d.ts.map +1 -1
  80. package/lib/types/server/site.d.ts.map +1 -1
  81. package/lib/types/server/tools.d.ts.map +1 -1
  82. package/lib/types/server/types.d.ts +22 -0
  83. package/lib/types/server/types.d.ts.map +1 -1
  84. package/lib/types/server/widgets.d.ts +0 -5
  85. package/lib/types/server/widgets.d.ts.map +1 -1
  86. package/lib/types/server.d.ts.map +1 -1
  87. package/lib/types/site/styles.d.ts +1 -1
  88. package/lib/types/site/styles.d.ts.map +1 -1
  89. package/lib/types/site/templates.d.ts +5 -0
  90. package/lib/types/site/templates.d.ts.map +1 -1
  91. package/lib/types/types.d.ts +29 -16
  92. package/lib/types/types.d.ts.map +1 -1
  93. package/lib/types/utils.d.ts +6 -0
  94. package/lib/types/utils.d.ts.map +1 -1
  95. package/package.json +5 -5
  96. package/src/ContentTypesCollection.ts +51 -0
  97. package/src/InteractionCollection.ts +1 -94
  98. package/src/SkillCollection.ts +13 -3
  99. package/src/ToolCollection.ts +11 -18
  100. package/src/ToolRegistry.ts +22 -55
  101. package/src/index.ts +1 -0
  102. package/src/server/app-package.ts +147 -0
  103. package/src/server/content-types.ts +89 -0
  104. package/src/server/interactions.ts +29 -3
  105. package/src/server/site.ts +9 -0
  106. package/src/server/tools.ts +3 -22
  107. package/src/server/types.ts +26 -0
  108. package/src/server/widgets.ts +2 -6
  109. package/src/server.ts +5 -0
  110. package/src/site/styles.ts +55 -0
  111. package/src/site/templates.ts +107 -84
  112. package/src/types.ts +33 -16
  113. package/src/utils.ts +15 -1
@@ -1,27 +1,16 @@
1
+ import { AgentToolDefinition } from "@vertesia/common";
1
2
  import { HTTPException } from "hono/http-exception";
2
- import { Tool, ToolDefinitionWithDefault, ToolExecutionContext, ToolExecutionPayload, ToolExecutionResult } from "./types.js";
3
+ import { Tool, ToolExecutionContext, ToolExecutionPayload, ToolExecutionResult, ToolUseContext } from "./types.js";
3
4
 
4
- /**
5
- * Options for filtering tool definitions
6
- */
7
- export interface ToolFilterOptions {
8
- /**
9
- * If true, only return tools that are available by default (default !== false).
10
- * If false or undefined, return all tools.
11
- */
12
- defaultOnly?: boolean;
13
- /**
14
- * List of tool names that are unlocked (available even if default: false).
15
- * These tools will be included even when defaultOnly is true.
16
- */
17
- unlockedTools?: string[];
18
- }
19
5
 
20
6
  export class ToolRegistry {
21
7
 
8
+ // The category name usinfg this registry
9
+ category: string;
22
10
  registry: Record<string, Tool<any>> = {};
23
11
 
24
- constructor(tools: Tool<any>[] = []) {
12
+ constructor(category: string, tools: Tool<any>[] = []) {
13
+ this.category = category;
25
14
  for (const tool of tools) {
26
15
  this.registry[tool.name] = tool;
27
16
  }
@@ -32,44 +21,22 @@ export class ToolRegistry {
32
21
  * @param options - Filtering options
33
22
  * @returns Filtered tool definitions
34
23
  */
35
- getDefinitions(options?: ToolFilterOptions): ToolDefinitionWithDefault[] {
36
- const { defaultOnly, unlockedTools = [] } = options || {};
37
- const unlockedSet = new Set(unlockedTools);
38
-
39
- return Object.values(this.registry)
40
- .filter(tool => {
41
- // If not filtering by default, include all tools
42
- if (!defaultOnly) return true;
43
-
44
- // Include if tool is default (default !== false) or is in unlocked list
45
- const isDefault = tool.default !== false;
46
- const isUnlocked = unlockedSet.has(tool.name);
47
- return isDefault || isUnlocked;
48
- })
49
- .map(tool => ({
50
- name: tool.name,
51
- description: tool.description,
52
- input_schema: tool.input_schema,
53
- default: tool.default,
54
- }));
55
- }
56
-
57
- /**
58
- * Get tools that are in reserve (default: false and not unlocked).
59
- * @param unlockedTools - List of tool names that are unlocked
60
- * @returns Tool definitions for reserve tools
61
- */
62
- getReserveTools(unlockedTools: string[] = []): ToolDefinitionWithDefault[] {
63
- const unlockedSet = new Set(unlockedTools);
64
-
65
- return Object.values(this.registry)
66
- .filter(tool => tool.default === false && !unlockedSet.has(tool.name))
67
- .map(tool => ({
68
- name: tool.name,
69
- description: tool.description,
70
- input_schema: tool.input_schema,
71
- default: tool.default,
72
- }));
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);
73
40
  }
74
41
 
75
42
  getTool<ParamsT extends Record<string, any>>(name: string): Tool<ParamsT> {
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { authorize, AuthSession } from "./auth.js";
2
+ export * from "./ContentTypesCollection.js";
2
3
  export { copyRuntimeAssets } from "./copy-assets.js";
3
4
  export * from "./InteractionCollection.js";
4
5
  export * from "./server.js";
@@ -0,0 +1,147 @@
1
+ import { AppPackage, AppPackageScope, AppWidgetInfo, CatalogInteractionRef, InCodeTypeDefinition } from "@vertesia/common";
2
+ import { Context, Hono } from "hono";
3
+ import { ToolUseContext } from "../types.js";
4
+ import { ToolServerConfig } from "./types.js";
5
+
6
+ function getRequestPayload<T>(c: Context): Promise<T | undefined> {
7
+ return c.req.method === "POST" ? c.req.json<T>() : Promise.resolve(undefined);
8
+ }
9
+
10
+ const builders: Record<Exclude<AppPackageScope, 'all'>, (pkg: AppPackage, config: ToolServerConfig, c: Context) => Promise<void>> = {
11
+ async tools(pkg: AppPackage, config: ToolServerConfig, c: Context) {
12
+ const { tools: toolCollections = [], skills: skillCollections = [] } = config;
13
+
14
+ const filterContext = await getRequestPayload<ToolUseContext>(c);
15
+
16
+ // Aggregate all tools from all collections
17
+ const allTools = toolCollections.flatMap(collection =>
18
+ collection.getToolDefinitions(filterContext)
19
+ );
20
+
21
+ // same for skills
22
+ const allSkills = skillCollections.flatMap(collection =>
23
+ collection.getToolDefinitions(filterContext)
24
+ );
25
+
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
+ });
37
+ },
38
+ async interactions(pkg: AppPackage, config: ToolServerConfig) {
39
+ const allInteractions: CatalogInteractionRef[] = [];
40
+ for (const coll of (config.interactions || [])) {
41
+ for (const inter of coll.interactions) {
42
+ allInteractions.push({
43
+ type: "app",
44
+ id: coll.name + ":" + inter.name,
45
+ name: inter.name,
46
+ title: inter.title || inter.name,
47
+ description: inter.description,
48
+ tags: inter.tags || [],
49
+ });
50
+ }
51
+ }
52
+ pkg.interactions = allInteractions;
53
+ },
54
+ async types(pkg: AppPackage, config: ToolServerConfig) {
55
+ const allTypes: InCodeTypeDefinition[] = [];
56
+ for (const coll of config.types || []) {
57
+ for (const type of coll.types) {
58
+ allTypes.push({
59
+ ...type,
60
+ id: coll.name + ":" + type.name
61
+ });
62
+ }
63
+ }
64
+ pkg.types = allTypes;
65
+ },
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) {
83
+ if (config.uiConfig) {
84
+ pkg.ui = { ...config.uiConfig };
85
+ const origin = new URL(c.req.url).origin;
86
+ pkg.ui.src = new URL(pkg.ui.src, origin).toString();
87
+ if (!pkg.ui.isolation) {
88
+ pkg.ui.isolation = "shadow";
89
+ }
90
+ }
91
+ },
92
+ async settings(pkg: AppPackage, config: ToolServerConfig) {
93
+ if (config.settings) {
94
+ pkg.settings_schema = { ...config.settings };
95
+ }
96
+ },
97
+ }
98
+
99
+
100
+ async function handlePackageRequest(c: Context, config: ToolServerConfig) {
101
+ const scope = c.req.query('scope') || 'all';
102
+ const pkg: AppPackage = {};
103
+
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')) {
130
+ builders.settings(pkg, config, c);
131
+ }
132
+ }
133
+
134
+ return c.json(pkg);
135
+ }
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
+ }
@@ -0,0 +1,89 @@
1
+ // ================== Content Type Endpoints ==================
2
+
3
+ import { InCodeTypeDefinition } from "@vertesia/common";
4
+ import { Context, Hono } from "hono";
5
+ import { HTTPException } from "hono/http-exception";
6
+ import { ContentTypesCollection } from "../ContentTypesCollection.js";
7
+ import { ToolServerConfig } from "./types.js";
8
+ import { toPathName } from "../utils.js";
9
+
10
+ export function createContentTypesRoute(app: Hono, basePath: string, config: ToolServerConfig) {
11
+ const { types = [] } = config;
12
+
13
+ // GET /api/types - Returns all interactions from all collections
14
+ app.get(basePath, (c) => {
15
+ const allTypes: InCodeTypeDefinition[] = [];
16
+
17
+ for (const coll of types) {
18
+ for (const type of coll.types) {
19
+ allTypes.push({ ...type, id: coll.name + ":" + toPathName(type.name) });
20
+ }
21
+ }
22
+
23
+ return c.json({
24
+ title: 'All Content Types',
25
+ description: 'All available content types across all collections',
26
+ types: allTypes,
27
+ collections: types.map(i => ({
28
+ name: i.name,
29
+ title: i.title,
30
+ description: i.description,
31
+ })),
32
+ });
33
+ });
34
+
35
+ // Create interaction collection endpoints
36
+ for (const coll of types) {
37
+ app.route(`${basePath}/${coll.name}`, createContentTypeEndpoints(coll));
38
+ }
39
+
40
+ // GET /api/types/:name - Direct access to content type
41
+ app.get(`${basePath}/:name`, async (c) => {
42
+ const name = c.req.param('name');
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 });
54
+ }
55
+
56
+ throw new HTTPException(404, {
57
+ message: "No content type found with name: " + name
58
+ });
59
+ });
60
+
61
+ }
62
+
63
+
64
+
65
+
66
+ function createContentTypeEndpoints(coll: ContentTypesCollection): Hono {
67
+ const endpoint = new Hono();
68
+
69
+ endpoint.get('/', (c: Context) => {
70
+ return c.json(coll.types.map(t => ({ ...t, id: coll.name + ":" + toPathName(t.name) })));
71
+ });
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
+ });
86
+
87
+
88
+ return endpoint;
89
+ }
@@ -18,7 +18,7 @@ export function createInteractionsRoute(app: Hono, basePath: string, config: Too
18
18
  for (const inter of coll.interactions) {
19
19
  allInteractions.push({
20
20
  type: "app",
21
- id: inter.name,
21
+ id: coll.name + ":" + inter.name,
22
22
  name: inter.name,
23
23
  title: inter.title || inter.name,
24
24
  description: inter.description,
@@ -44,6 +44,29 @@ export function createInteractionsRoute(app: Hono, basePath: string, config: Too
44
44
  app.route(`${basePath}/${coll.name}`, createInteractionEndpoints(coll));
45
45
  }
46
46
 
47
+ // GET /api/interactions/:name - Direct access to interaction by name (searches all collections)
48
+ app.get(`${basePath}/:name`, async (c) => {
49
+ await authorize(c);
50
+ const name = c.req.param('name');
51
+
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 });
63
+ }
64
+
65
+ throw new HTTPException(404, {
66
+ message: "No interaction found with name: " + name
67
+ });
68
+ });
69
+
47
70
  }
48
71
 
49
72
 
@@ -55,7 +78,7 @@ function createInteractionEndpoints(coll: InteractionCollection): Hono {
55
78
  endpoint.get('/', (c: Context) => {
56
79
  return c.json(coll.interactions.map(inter => ({
57
80
  type: "app",
58
- id: inter.name,
81
+ id: coll.name + ":" + inter.name,
59
82
  name: inter.name,
60
83
  title: inter.title || inter.name,
61
84
  description: inter.description,
@@ -72,7 +95,10 @@ function createInteractionEndpoints(coll: InteractionCollection): Hono {
72
95
  message: "No interaction found with name: " + name
73
96
  });
74
97
  }
75
- return c.json(inter);
98
+ return c.json({
99
+ ...inter,
100
+ id: coll.name + ":" + inter.name,
101
+ });
76
102
  });
77
103
 
78
104
  return endpoint;
@@ -1,5 +1,6 @@
1
1
  import { Hono } from "hono";
2
2
  import {
3
+ contentTypeCollectionPage,
3
4
  indexPage,
4
5
  interactionCollectionPage,
5
6
  skillCollectionPage,
@@ -12,6 +13,7 @@ export function createSiteRoute(app: Hono, basePath: string, config: ToolServerC
12
13
  const {
13
14
  tools = [],
14
15
  interactions = [],
16
+ types = [],
15
17
  skills = [],
16
18
  } = config;
17
19
 
@@ -41,4 +43,11 @@ export function createSiteRoute(app: Hono, basePath: string, config: ToolServerC
41
43
  });
42
44
  }
43
45
 
46
+ // Content type collection pages
47
+ for (const coll of types) {
48
+ app.get(`${basePath}/types/${coll.name}`, (c) => {
49
+ return c.html(contentTypeCollectionPage(coll));
50
+ });
51
+ }
52
+
44
53
  }
@@ -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
 
@@ -3,6 +3,9 @@ import { InteractionCollection } from "../InteractionCollection.js";
3
3
  import { SkillCollection } from "../SkillCollection.js";
4
4
  import { ToolCollection } from "../ToolCollection.js";
5
5
  import { ToolExecutionPayload } from "../types.js";
6
+ import { JSONSchema } from "@llumiverse/common";
7
+ import { AppUIConfig, ProjectConfiguration } from "@vertesia/common";
8
+ import { ContentTypesCollection } from "../ContentTypesCollection.js";
6
9
 
7
10
  /**
8
11
  * Extended context with parsed payload for tool/skill execution
@@ -49,6 +52,10 @@ export interface ToolServerConfig {
49
52
  * Interaction collections to expose
50
53
  */
51
54
  interactions?: InteractionCollection[];
55
+ /**
56
+ * Content type collections to expose
57
+ */
58
+ types?: ContentTypesCollection[];
52
59
  /**
53
60
  * Skill collections to expose
54
61
  */
@@ -57,6 +64,17 @@ export interface ToolServerConfig {
57
64
  * MCP providers to expose
58
65
  */
59
66
  mcpProviders?: MCPProviderConfig[];
67
+
68
+ /**
69
+ * A JSON schema defining settings for the application using this server
70
+ */
71
+ settings?: JSONSchema;
72
+
73
+ /**
74
+ * The UI configuration for the application using this server
75
+ */
76
+ uiConfig?: AppUIConfig;
77
+
60
78
  /**
61
79
  * Disable HTML pages (default: false)
62
80
  */
@@ -65,5 +83,13 @@ export interface ToolServerConfig {
65
83
  * Hide UI app links on the index page (default: false)
66
84
  */
67
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;
68
94
  }
69
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
@@ -10,6 +10,8 @@ import { createToolsRoute } from "./server/tools.js";
10
10
  import { ToolContext, ToolServerConfig } from "./server/types.js";
11
11
  import { ToolExecutionPayload } from "./types.js";
12
12
  import { createWidgetsRoute } from "./server/widgets.js";
13
+ import { createPackageRoute } from "./server/app-package.js";
14
+ import { createContentTypesRoute } from "./server/content-types.js";
13
15
 
14
16
  // Schema for tool execution payload
15
17
  const ToolExecutionPayloadSchema = z.object({
@@ -98,10 +100,13 @@ export function createToolServer(config: ToolServerConfig): Hono {
98
100
  });
99
101
  });
100
102
 
103
+
104
+ createPackageRoute(app, `${prefix}/package`, config);
101
105
  createToolsRoute(app, `${prefix}/tools`, config);
102
106
  createSkillsRoute(app, `${prefix}/skills`, config);
103
107
  createWidgetsRoute(app, `${prefix}/widgets`, config);
104
108
  createInteractionsRoute(app, `${prefix}/interactions`, config);
109
+ createContentTypesRoute(app, `${prefix}/types`, config);
105
110
  createMcpRoute(app, `${prefix}/mcp`, config);
106
111
 
107
112
 
@@ -396,6 +396,43 @@ hr {
396
396
  font-family: ui-monospace, monospace;
397
397
  }
398
398
 
399
+ .endpoint-box {
400
+ display: flex;
401
+ align-items: center;
402
+ gap: 0.75rem;
403
+ background: #f3f4f6;
404
+ padding: 0.75rem 1rem;
405
+ border-radius: 8px;
406
+ margin-top: 0.5rem;
407
+ }
408
+
409
+ .endpoint-box code {
410
+ flex: 1;
411
+ font-family: ui-monospace, monospace;
412
+ font-size: 0.875rem;
413
+ color: #1f2937;
414
+ }
415
+
416
+ .copy-btn {
417
+ background: #e5e7eb;
418
+ border: none;
419
+ padding: 0.5rem;
420
+ border-radius: 6px;
421
+ cursor: pointer;
422
+ color: #6b7280;
423
+ transition: all 0.15s;
424
+ }
425
+
426
+ .copy-btn:hover {
427
+ background: #d1d5db;
428
+ color: #374151;
429
+ }
430
+
431
+ .copy-btn svg {
432
+ width: 16px;
433
+ height: 16px;
434
+ }
435
+
399
436
  .badge {
400
437
  display: inline-block;
401
438
  font-size: 0.75rem;
@@ -583,6 +620,24 @@ hr {
583
620
  background: rgba(15, 23, 42, 0.95);
584
621
  }
585
622
 
623
+ .endpoint-box {
624
+ background: rgba(31, 41, 55, 0.95);
625
+ }
626
+
627
+ .endpoint-box code {
628
+ color: #e5e7eb;
629
+ }
630
+
631
+ .copy-btn {
632
+ background: rgba(55, 65, 81, 0.95);
633
+ color: #e5e7eb;
634
+ }
635
+
636
+ .copy-btn:hover {
637
+ background: rgba(75, 85, 99, 0.98);
638
+ color: #f9fafb;
639
+ }
640
+
586
641
  .badge {
587
642
  background: rgba(15, 23, 42, 0.96);
588
643
  color: #e5e7eb;