A template for building custom tool servers with tools, skills, and interactions
npm install tool-server-templateA template for building custom tool servers that expose LLM tools, skills, interactions, and MCP providers. Built with Hono for flexible deployment to Vercel Functions or Node.js HTTP servers.
- š ļø Tools: Executable functions that can be invoked via API (e.g., calculator, API integrations)
- šÆ Skills: AI capabilities defined as markdown prompts with optional helper scripts
- š Interactions: Multi-step agent workflows with templated prompts
- š MCP Providers: Model Context Protocol integrations (optional)
- š Auto-generated HTML: Browse and explore resources with automatically generated pages
- š Flexible Deployment: Deploy to Vercel Functions, Cloud Run, Railway, or any Node.js host
- š¦ Browser Bundles: Standalone browser-ready bundles for client-side usage
- š§ Simple Build: Single Rollup config handles TypeScript, raw imports, and bundling
```
tool-server-template/
āāā src/
ā āāā tools/ # Tool collections
ā ā āāā calculator/ # Example: calculator tool
ā ā āāā manifest.ts
ā ā āāā calculator.ts
ā ā āāā icon.svg.ts
ā ā āāā index.ts
ā āāā skills/ # Skill collections
ā ā āāā code-review/ # Example: code review skill
ā ā āāā SKILL.md
ā āāā interactions/ # Interaction collections
ā ā āāā summarize/ # Example: text summarization
ā ā āāā text_summarizer/
ā ā āāā prompt.jst
ā ā āāā index.ts
ā āāā server.ts # Hono server entry point
ā āāā build-site.ts # Static HTML generator
āāā api/
ā āāā index.js # Vercel adapter
āāā lib/ # Compiled code (TypeScript ā JavaScript)
ā āāā server.js
ā āāā server-node.js
ā āāā tools/
ā āāā ...
āāā dist/ # Static HTML pages (public files)
ā āāā index.html
ā āāā tools/
ā āāā skills/
ā āāā interactions/
āāā public/ # Static assets
āāā package.json
āāā rollup.config.js # Unified build configuration (TypeScript + bundles)
āāā vercel.json # Vercel deployment config
āāā tsconfig.json
- Node.js 18+
- npm or pnpm
`bash`Install dependencies
npm installor
pnpm install
Start the development server with automatic rebuild and restart:
`bash`
npm run dev
This will:
1. Initial build - Compiles TypeScript and generates HTML pages
2. Rollup watch mode - Rebuilds TypeScript on file changes
3. Node.js with --watch - Restarts server when lib/ changes
The server will be available at:
- API: http://localhost:3000/api
- Web UI: http://localhost:3000
To use a different port:
`bash`
PORT=8080 npm run dev
Manual control (advanced):
`bashTerminal 1: Build on changes
npm run build:watch
$3
Build the project for production:
`bash
npm run build
`This will:
1. Rollup: Compile TypeScript to JavaScript in
lib/ (ESM with preserveModules)
2. Copy assets: Copy skill assets (.md, .py files) to lib/
3. Generate HTML: Create static HTML pages in dist/
4. Rollup: Create browser bundles in dist/libs/The build uses a single rollup.config.js that handles:
- TypeScript compilation with
@rollup/plugin-typescript ā lib/
- ?raw imports for template files (via custom rawPlugin)
- Browser bundles with tree-shaking and minification ā dist/libs/Output structure:
-
lib/ = Compiled code (what you run)
- dist/ = Static HTML + browser bundles (what you serve)Creating Resources
$3
Tools are executable functions that can be invoked via API.
Structure:
`
src/tools/my-tool/
āāā manifest.ts # Tool metadata and schema
āāā my-tool.ts # Implementation
āāā icon.svg.ts # SVG icon
āāā index.ts # Collection export
`Example: manifest.ts
`typescript
import { ToolDefinition } from "@vertesia/tools-sdk";export default {
name: "my-tool",
description: "Description of what this tool does",
input_schema: {
type: "object",
properties: {
param1: {
type: "string",
description: "First parameter"
}
},
required: ["param1"]
}
} satisfies ToolDefinition;
`Example: my-tool.ts
`typescript
import { Tool, ToolExecutionContext } from "@vertesia/tools-sdk";
import manifest from "./manifest.js";interface MyToolParams {
param1: string;
}
async function execute(
params: MyToolParams,
context: ToolExecutionContext
): Promise {
// Tool implementation
return
Processed: ${params.param1};
}export const MyTool = {
...manifest,
run: execute
} satisfies Tool;
`Example: index.ts
`typescript
import { ToolCollection } from "@vertesia/tools-sdk";
import { MyTool } from "./my-tool.js";
import icon from "./icon.svg.js";export const MyTools = new ToolCollection({
name: "my-tool",
title: "My Tools",
description: "Description of the tool collection",
icon,
tools: [MyTool]
});
`Register the collection:
Add to
src/tools/index.ts:
`typescript
import { MyTools } from "./my-tool/index.js";export const tools = [
MyTools,
// ... other collections
];
`$3
Skills are AI capabilities defined as markdown prompts.
Structure:
`
src/skills/my-skill/
āāā SKILL.md # Skill definition
āāā helper.py # Optional helper script
`Example: SKILL.md
`markdown
---
name: my-skill
title: My Skill
keywords: keyword1, keyword2, keyword3
tools: tool1, tool2
packages: package1==1.0.0
---My Skill
You are an AI assistant with expertise in [domain].
Instructions
1. First instruction
2. Second instruction
3. Third instruction
Guidelines
- Guideline 1
- Guideline 2
`Register the collection:
Create
src/skills/my-skill/index.ts:
`typescript
import { SkillCollection, loadSkillsFromDirectory } from "@vertesia/tools-sdk";export const MySkills = new SkillCollection({
name: "my-skill",
title: "My Skills",
description: "Description of the skill collection",
skills: loadSkillsFromDirectory(new URL(".", import.meta.url).pathname)
});
`Add to
src/skills/index.ts:
`typescript
import { MySkills } from "./my-skill/index.js";export const skills = [
MySkills,
// ... other collections
];
`$3
Interactions are multi-step workflows with templated prompts.
Structure:
`
src/interactions/my-interaction/
āāā my_workflow/
āāā prompt.jst # JavaScript template string
āāā index.ts # Interaction spec
`Example: prompt.jst
`javascript
return Please process the following according to the parameters above.
${additionalInstructions || 'No additional instructions.'};`
Example: index.ts
`typescript
import { PromptRole } from "@llumiverse/common";
import { InteractionSpec, TemplateType } from "@vertesia/common";
import PROMPT_CONTENT from "./prompt.jst?raw";
export default {
name: "my_workflow",
title: "My Workflow",
description: "Description of what this interaction does",
result_schema: {
type: "object",
properties: {
result: {
type: "string",
description: "The workflow result"
}
},
required: ["result"]
},
prompts: [{
role: PromptRole.user,
content: PROMPT_CONTENT,
content_type: TemplateType.jst,
schema: {
type: "object",
properties: {
taskName: { type: "string" },
input: { type: "string" },
options: { type: "object" },
additionalInstructions: { type: "string" }
},
required: ["taskName", "input"]
}
}],
tags: ["tag1", "tag2"]
} satisfies InteractionSpec;
`
Register the collection:
Create src/interactions/my-interaction/index.ts:`typescript
import { InteractionCollection } from "@vertesia/tools-sdk";
import myWorkflow from "./my_workflow/index.js";
import icon from "./icon.svg.js";
export const MyInteractions = new InteractionCollection({
name: "my-interaction",
title: "My Interactions",
description: "Description of the interaction collection",
icon,
interactions: [myWorkflow]
});
`
Add to src/interactions/index.ts:`typescript
import { MyInteractions } from "./my-interaction/index.js";
export async function loadInteractions() {
return [
MyInteractions,
// ... other collections
];
}
`
#### GET /api
Returns descriptions of all available tools, skills, and interactions.
Response:
`json`
{
"tools": [...],
"skills": [...],
"interactions": [...]
}
#### POST /api
Executes a tool with the provided payload.
Request Body:
`json`
{
"tool_name": "calculator",
"tool_input": {
"expression": "2 + 2"
},
"context": {
"serverUrl": "http://localhost:5174",
"storeUrl": "http://store.example.com",
"apikey": "your-api-key"
},
"vars": {}
}
Response:
`json`
{
"is_error": false,
"content": "Result: 2 + 2 = 4"
}
The template supports two deployment modes:
Best for: Auto-scaling, zero-config deployment
`bashInstall Vercel CLI
npm i -g vercel
The
api/index.js adapter automatically converts your Hono server to Vercel Functions format.$3
Best for: Cloud Run, Railway, Fly.io, Docker, VPS
The template includes
src/server-node.ts which creates a standalone HTTP server.Deploy to Cloud Run:
`bash
gcloud run deploy tool-server \
--source . \
--platform managed \
--region us-central1
`Deploy to Railway:
1. Connect your repo
2. Railway auto-detects Node.js
3. Uses
npm start automaticallyDeploy to Docker:
`dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
`Deploy to VPS:
`bash
On your server
git clone
cd tool-server-template
npm install
npm run build
npm startOr use PM2 for process management
npm i -g pm2
pm2 start lib/server-node.js --name tool-server
`$3
Hono's flexibility allows deployment to:
- Cloudflare Workers
- Deno Deploy
- AWS Lambda (with adapter)
- Bun
- Azure Functions
See Hono documentation for platform-specific guides.
Configuration
$3
Edit
src/server.ts to customize:`typescript
const server = createToolServer({
title: 'Your Server Name',
description: 'Your server description',
prefix: '/api',
tools,
interactions,
skills,
mcpProviders: [] // Add MCP providers here
});
`$3
For GitHub integration or other sensitive config, use environment variables:
1. Create
.env file:
`bash
GITHUB_APP_ID=your-app-id
GITHUB_APP_PRIVATE_KEY_FILE=path/to/key.pem
`2. Access in code:
`typescript
const githubAppId = process.env.GITHUB_APP_ID;
`Browser Bundles
After building, browser-ready bundles are available at:
`
dist/libs/tool-server-{collection-name}.js
`Use them in the browser:
`html
`Debugging
This template includes full VSCode debugging support with breakpoints, watch expressions, and call stack inspection.
Quick start:
1. Press F5 in VSCode
2. Select "Debug Server"
3. Set breakpoints in your TypeScript files
4. Make API requests to trigger breakpoints
For complete debugging workflows, configurations, and troubleshooting, see .vscode/README.md.
Development Tips
1. Watch Mode:
npm run dev automatically rebuilds and restarts on file changes
2. Type Safety: Use satisfies to ensure type correctness while preserving inference
3. Raw Imports: Use import content from './file.jst?raw' for large template strings
4. Debugging: See .vscode/README.md for VSCode debugging setup
5. Testing Tools: Use POST /api with curl or Postman to test tools$3
Test a tool:
`bash
curl -H "Authorization: Bearer {{VERTESIA_JWT}}" \
-H "Content-Type: application/json" \
-X POST "http://localhost:3000/api/tools/calculator" \
-d '{
"tool_use": {
"id": "run1",
"tool_name": "calculator",
"tool_input": {"expression": "10 * 5"}
}
}'
`Get interaction details:
`bash
curl -H "Authorization: Bearer {{VERTESIA_JWT}}" \
"http://localhost:3000/api/interactions/summarize/text_summarizer"
`Get skill details:
`bash
curl -H "Authorization: Bearer {{VERTESIA_JWT}}" \
"http://localhost:3000/api/skills/code-review/skill_code-review"
`Replace
{{VERTESIA_JWT}} with a valid Vertesia JWT token.Troubleshooting
$3
- Ensure all imports use .js extensions (ESM requirement)
- Check that tsconfig.json has "module": "ES2022"
- Verify @rollup/plugin-typescript is installed$3
- Make sure concurrently is installed
- Check that dependencies are installed (npm install)
- If still having issues, try npm run build` manually firstMIT
Contributions are welcome! Please feel free to submit issues or pull requests.
---
Built with ā¤ļø using Hono and @vertesia/tools-sdk