create-bk-agent 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -0
- package/dist/bin/create-bk-agent.js +8 -0
- package/dist/src/index.js +83 -0
- package/dist/src/prompts.js +54 -0
- package/dist/src/scaffold.js +101 -0
- package/package.json +33 -0
- package/templates/autonomous/.env.example +8 -0
- package/templates/autonomous/package.json +23 -0
- package/templates/autonomous/src/index.ts +64 -0
- package/templates/autonomous/tsconfig.json +15 -0
- package/templates/coding/.env.example +8 -0
- package/templates/coding/AGENT.md +10 -0
- package/templates/coding/package.json +24 -0
- package/templates/coding/src/index.ts +48 -0
- package/templates/coding/tsconfig.json +16 -0
- package/templates/domain-agent/.env.example +7 -0
- package/templates/domain-agent/package.json +25 -0
- package/templates/domain-agent/src/agents/orchestrator.ts +22 -0
- package/templates/domain-agent/src/agents/specialist-a.ts +17 -0
- package/templates/domain-agent/src/agents/specialist-b.ts +16 -0
- package/templates/domain-agent/src/config.ts +19 -0
- package/templates/domain-agent/src/engine.ts +49 -0
- package/templates/domain-agent/src/index.ts +27 -0
- package/templates/domain-agent/src/providers/openai-compatible.ts +104 -0
- package/templates/domain-agent/src/tools/example-tools.ts +28 -0
- package/templates/domain-agent/tsconfig.json +16 -0
- package/templates/mcp-server/package.json +26 -0
- package/templates/mcp-server/src/server.ts +40 -0
- package/templates/mcp-server/src/tools/example.ts +10 -0
- package/templates/mcp-server/tsconfig.json +15 -0
- package/templates/minimal/package.json +23 -0
- package/templates/minimal/src/index.ts +40 -0
- package/templates/minimal/tsconfig.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# create-bk-agent
|
|
2
|
+
|
|
3
|
+
Scaffold a new [BackendKit agent](https://github.com/BackendKit-labs/backendkit-agent-framework) project in seconds.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx create-bk-agent my-project
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
✨ create-bk-agent v0.14.0
|
|
13
|
+
|
|
14
|
+
? Project name: my-project
|
|
15
|
+
? Template: coding — Coding assistant (files, git, code execution)
|
|
16
|
+
? LLM provider: Anthropic (Claude)
|
|
17
|
+
|
|
18
|
+
Creating coding agent in ./my-project...
|
|
19
|
+
Installing dependencies...
|
|
20
|
+
|
|
21
|
+
✅ Done!
|
|
22
|
+
cd my-project
|
|
23
|
+
cp .env.example .env
|
|
24
|
+
npm run dev
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Templates
|
|
28
|
+
|
|
29
|
+
| Template | Use case |
|
|
30
|
+
|----------|---------|
|
|
31
|
+
| `coding` | Coding assistant — edit files, run commands, search code |
|
|
32
|
+
| `domain-agent` | Multi-agent system — orchestrator + domain specialists |
|
|
33
|
+
| `autonomous` | Autonomous agent — cron schedules, webhooks, events |
|
|
34
|
+
| `mcp-server` | MCP server — expose tools via Model Context Protocol |
|
|
35
|
+
| `minimal` | Bare engine — full control over everything |
|
|
36
|
+
|
|
37
|
+
## Providers
|
|
38
|
+
|
|
39
|
+
Supports Anthropic (Claude), OpenAI, DeepSeek / OpenAI-compatible endpoints, and Ollama (local).
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT © [BackendKit Labs](https://github.com/BackendKit-labs)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const index_1 = require("../src/index");
|
|
5
|
+
(0, index_1.main)(process.argv).catch((err) => {
|
|
6
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
7
|
+
process.exit(1);
|
|
8
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.main = main;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const prompts_1 = require("./prompts");
|
|
40
|
+
const scaffold_1 = require("./scaffold");
|
|
41
|
+
const FRAMEWORK_VERSION = '0.14.0';
|
|
42
|
+
const NEXT_STEPS = {
|
|
43
|
+
coding: ['Edit AGENT.md to describe your project', 'npm run dev'],
|
|
44
|
+
'domain-agent': ['Rename agents/ and tools/ to match your domain', 'Implement tools in src/tools/', 'npm run dev'],
|
|
45
|
+
autonomous: ['Configure triggers in src/index.ts', 'npm run dev (or npm start in production)'],
|
|
46
|
+
'mcp-server': ['Register tools in src/server.ts', 'npm run build && npx my-mcp-server'],
|
|
47
|
+
minimal: ['Implement your LLMProvider in src/index.ts', 'npm run dev'],
|
|
48
|
+
};
|
|
49
|
+
async function main(argv) {
|
|
50
|
+
const projectArg = argv[2];
|
|
51
|
+
console.log(`\n✨ create-bk-agent v${FRAMEWORK_VERSION}\n`);
|
|
52
|
+
const defaultName = projectArg ?? 'my-agent';
|
|
53
|
+
const choices = await (0, prompts_1.askQuestions)(defaultName);
|
|
54
|
+
if (!choices) {
|
|
55
|
+
console.log('\nCancelled.');
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
const targetDir = path.resolve(process.cwd(), choices.projectName);
|
|
59
|
+
if (fs.existsSync(targetDir)) {
|
|
60
|
+
console.error(`\n✗ Directory already exists: ${targetDir}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
console.log(`\nCreating ${choices.template} agent in ./${choices.projectName} ...`);
|
|
64
|
+
try {
|
|
65
|
+
(0, scaffold_1.scaffold)(choices, targetDir);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
console.error(`\n✗ ${err.message}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
console.log('Installing dependencies...\n');
|
|
72
|
+
(0, scaffold_1.installDeps)(targetDir);
|
|
73
|
+
const steps = NEXT_STEPS[choices.template] ?? ['npm run dev'];
|
|
74
|
+
console.log(`
|
|
75
|
+
✅ Done! Your agent is ready.
|
|
76
|
+
|
|
77
|
+
cd ${choices.projectName}
|
|
78
|
+
cp .env.example .env # add your API key
|
|
79
|
+
${steps.map(s => ` ${s}`).join('\n')}
|
|
80
|
+
|
|
81
|
+
Documentation: https://github.com/BackendKit-labs/backendkit-agent-framework
|
|
82
|
+
`);
|
|
83
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.askQuestions = askQuestions;
|
|
7
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
8
|
+
const TEMPLATES = [
|
|
9
|
+
{ title: 'coding', value: 'coding', description: 'Coding assistant — edit files, run commands, search code' },
|
|
10
|
+
{ title: 'domain-agent', value: 'domain-agent', description: 'Multi-agent — orchestrator + domain specialists (e.g. docker, AWS)' },
|
|
11
|
+
{ title: 'autonomous', value: 'autonomous', description: 'Autonomous — activates on cron schedules, webhooks, events' },
|
|
12
|
+
{ title: 'mcp-server', value: 'mcp-server', description: 'MCP server — expose tools via Model Context Protocol' },
|
|
13
|
+
{ title: 'minimal', value: 'minimal', description: 'Minimal — bare engine, full control over everything' },
|
|
14
|
+
];
|
|
15
|
+
const PROVIDERS = [
|
|
16
|
+
{ title: 'Anthropic (Claude)', value: 'anthropic', description: 'claude-sonnet-4-6 recommended' },
|
|
17
|
+
{ title: 'OpenAI', value: 'openai', description: 'gpt-4o recommended' },
|
|
18
|
+
{ title: 'DeepSeek / OpenAI-compatible', value: 'deepseek', description: 'deepseek-chat or any OpenAI-compatible endpoint' },
|
|
19
|
+
{ title: 'Ollama (local)', value: 'ollama', description: 'llama3.2, codellama, qwen2.5 — no API key needed' },
|
|
20
|
+
];
|
|
21
|
+
async function askQuestions(defaultName) {
|
|
22
|
+
const answers = await (0, prompts_1.default)([
|
|
23
|
+
{
|
|
24
|
+
type: 'text',
|
|
25
|
+
name: 'projectName',
|
|
26
|
+
message: 'Project name:',
|
|
27
|
+
initial: defaultName,
|
|
28
|
+
validate: (v) => v.trim().length > 0 || 'Name cannot be empty',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
type: 'select',
|
|
32
|
+
name: 'template',
|
|
33
|
+
message: 'Template:',
|
|
34
|
+
choices: TEMPLATES.map(t => ({
|
|
35
|
+
title: t.title.padEnd(14) + ' ' + t.description,
|
|
36
|
+
value: t.value,
|
|
37
|
+
})),
|
|
38
|
+
initial: 0,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: 'select',
|
|
42
|
+
name: 'provider',
|
|
43
|
+
message: 'LLM provider:',
|
|
44
|
+
choices: PROVIDERS.map(p => ({
|
|
45
|
+
title: p.title.padEnd(30) + ' ' + p.description,
|
|
46
|
+
value: p.value,
|
|
47
|
+
})),
|
|
48
|
+
initial: 0,
|
|
49
|
+
},
|
|
50
|
+
], { onCancel: () => { return false; } });
|
|
51
|
+
if (!answers.projectName || !answers.template || !answers.provider)
|
|
52
|
+
return null;
|
|
53
|
+
return answers;
|
|
54
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.scaffold = scaffold;
|
|
37
|
+
exports.installDeps = installDeps;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const TEMPLATES_DIR = path.resolve(__dirname, '../../templates');
|
|
42
|
+
// ── Provider env var hints ────────────────────────────────────────────────────
|
|
43
|
+
const PROVIDER_ENV = {
|
|
44
|
+
anthropic: { varName: 'ANTHROPIC_API_KEY', placeholder: 'sk-ant-...', model: 'claude-sonnet-4-6' },
|
|
45
|
+
openai: { varName: 'OPENAI_API_KEY', placeholder: 'sk-...', model: 'gpt-4o' },
|
|
46
|
+
deepseek: { varName: 'LLM_API_KEY', placeholder: 'sk-...', model: 'deepseek-chat' },
|
|
47
|
+
ollama: { varName: 'OLLAMA_BASE_URL', placeholder: 'http://localhost:11434', model: 'llama3.2' },
|
|
48
|
+
};
|
|
49
|
+
// ── Copy template ─────────────────────────────────────────────────────────────
|
|
50
|
+
function copyDir(src, dst) {
|
|
51
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
52
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
53
|
+
const srcPath = path.join(src, entry.name);
|
|
54
|
+
const dstPath = path.join(dst, entry.name);
|
|
55
|
+
if (entry.isDirectory()) {
|
|
56
|
+
copyDir(srcPath, dstPath);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
fs.copyFileSync(srcPath, dstPath);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ── Substitute project name in package.json ───────────────────────────────────
|
|
64
|
+
function patchPackageJson(dir, projectName) {
|
|
65
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
66
|
+
if (!fs.existsSync(pkgPath))
|
|
67
|
+
return;
|
|
68
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
69
|
+
pkg.name = projectName;
|
|
70
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
71
|
+
}
|
|
72
|
+
// ── Write .env with provider preset ──────────────────────────────────────────
|
|
73
|
+
function writeEnv(dir, provider) {
|
|
74
|
+
const envExample = path.join(dir, '.env.example');
|
|
75
|
+
const envFile = path.join(dir, '.env');
|
|
76
|
+
// Only create .env if .env.example exists and .env doesn't
|
|
77
|
+
if (!fs.existsSync(envExample) || fs.existsSync(envFile))
|
|
78
|
+
return;
|
|
79
|
+
const p = PROVIDER_ENV[provider];
|
|
80
|
+
const content = `# Generated by create-bk-agent\n${p.varName}=${p.placeholder}\nLLM_PROVIDER=${provider}\nLLM_MODEL=${p.model}\n`;
|
|
81
|
+
fs.writeFileSync(envFile, content);
|
|
82
|
+
}
|
|
83
|
+
// ── Main scaffold ─────────────────────────────────────────────────────────────
|
|
84
|
+
function scaffold(choices, targetDir) {
|
|
85
|
+
const templateDir = path.join(TEMPLATES_DIR, choices.template);
|
|
86
|
+
if (!fs.existsSync(templateDir)) {
|
|
87
|
+
throw new Error(`Template not found: ${choices.template} (looked in ${templateDir})`);
|
|
88
|
+
}
|
|
89
|
+
if (fs.existsSync(targetDir)) {
|
|
90
|
+
throw new Error(`Directory already exists: ${targetDir}`);
|
|
91
|
+
}
|
|
92
|
+
// 1. Copy template files
|
|
93
|
+
copyDir(templateDir, targetDir);
|
|
94
|
+
// 2. Patch package.json name
|
|
95
|
+
patchPackageJson(targetDir, choices.projectName);
|
|
96
|
+
// 3. Write .env stub
|
|
97
|
+
writeEnv(targetDir, choices.provider);
|
|
98
|
+
}
|
|
99
|
+
function installDeps(targetDir) {
|
|
100
|
+
(0, child_process_1.execSync)('npm install', { cwd: targetDir, stdio: 'inherit' });
|
|
101
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-bk-agent",
|
|
3
|
+
"version": "0.15.0",
|
|
4
|
+
"description": "Create a new BackendKit agent project",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-bk-agent": "./dist/bin/create-bk-agent.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/",
|
|
11
|
+
"templates/"
|
|
12
|
+
],
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/BackendKit-labs/backendkit-agent-framework.git",
|
|
16
|
+
"directory": "packages/create-bk-agent"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": { "access": "public" },
|
|
19
|
+
"engines": { "node": ">=20.0.0" },
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"prepack": "node scripts/copy-templates.js && npm run build",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"prompts": "^2.4.2"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^20.0.0",
|
|
30
|
+
"@types/prompts": "^2.4.9",
|
|
31
|
+
"typescript": "^5.4.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-autonomous-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Autonomous agent — activates on cron, webhooks, or custom events",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "ts-node src/index.ts",
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"typecheck": "tsc --noEmit"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@backendkit-labs/agent-core": "^0.14.0",
|
|
15
|
+
"dotenv": "^16.0.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^20.0.0",
|
|
19
|
+
"ts-node": "^10.9.0",
|
|
20
|
+
"typescript": "^5.4.0"
|
|
21
|
+
},
|
|
22
|
+
"engines": { "node": ">=20.0.0" }
|
|
23
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { createAutonomousAgent } from '@backendkit-labs/agent-core';
|
|
3
|
+
import type { AgentProfile } from '@backendkit-labs/agent-core';
|
|
4
|
+
|
|
5
|
+
// ── Agent definition ──────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
const monitorAgent: AgentProfile = {
|
|
8
|
+
id: 'monitor',
|
|
9
|
+
name: 'Monitor',
|
|
10
|
+
icon: '👁',
|
|
11
|
+
description: 'Monitors events and executes scheduled tasks',
|
|
12
|
+
allowedTools: [],
|
|
13
|
+
delegatesTo: [],
|
|
14
|
+
systemPrompt: `You are an autonomous monitor agent.
|
|
15
|
+
// TODO: describe what this agent monitors and what actions it takes.
|
|
16
|
+
When activated by a trigger, analyze the input and take appropriate action.`,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// ── Provider ──────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const provider = process.env.LLM_PROVIDER ?? 'anthropic';
|
|
22
|
+
const apiKey = process.env.ANTHROPIC_API_KEY ?? process.env.OPENAI_API_KEY ?? '';
|
|
23
|
+
|
|
24
|
+
if (!apiKey) { console.error('Missing API key'); process.exit(1); }
|
|
25
|
+
|
|
26
|
+
// ── Autonomous agent ──────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
const agent = createAutonomousAgent({
|
|
29
|
+
providers: { [provider]: { apiKey } },
|
|
30
|
+
defaultProvider: provider,
|
|
31
|
+
agents: [monitorAgent],
|
|
32
|
+
defaultAgent: 'monitor',
|
|
33
|
+
goal: 'TODO: describe the agent goal',
|
|
34
|
+
|
|
35
|
+
triggers: [
|
|
36
|
+
// Run every day at 9:00 AM
|
|
37
|
+
{
|
|
38
|
+
type: 'schedule',
|
|
39
|
+
cron: '0 9 * * *',
|
|
40
|
+
task: 'Run daily check and report findings',
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
// Listen for webhook events (e.g. from GitHub, Slack, etc.)
|
|
44
|
+
{
|
|
45
|
+
type: 'webhook',
|
|
46
|
+
path: '/events',
|
|
47
|
+
// secret: process.env.WEBHOOK_SECRET, // uncomment to validate signatures
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// TODO: add more triggers as needed
|
|
51
|
+
// { type: 'event', topic: 'my.custom.event' },
|
|
52
|
+
],
|
|
53
|
+
|
|
54
|
+
webhookPort: parseInt(process.env.WEBHOOK_PORT ?? '3000'),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// ── Start ─────────────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
await agent.start();
|
|
60
|
+
console.log('Autonomous agent started.');
|
|
61
|
+
console.log('Webhook listening on port', process.env.WEBHOOK_PORT ?? 3000);
|
|
62
|
+
|
|
63
|
+
process.on('SIGTERM', async () => { await agent.stop(); process.exit(0); });
|
|
64
|
+
process.on('SIGINT', async () => { await agent.stop(); process.exit(0); });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"esModuleInterop": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"],
|
|
14
|
+
"exclude": ["node_modules", "dist"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-coding-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Coding assistant agent — edit files, run commands, search code",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "ts-node src/index.ts",
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"typecheck": "tsc --noEmit"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@backendkit-labs/agent-coding": "^0.14.0",
|
|
15
|
+
"@backendkit-labs/agent-core": "^0.14.0",
|
|
16
|
+
"dotenv": "^16.0.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^20.0.0",
|
|
20
|
+
"ts-node": "^10.9.0",
|
|
21
|
+
"typescript": "^5.4.0"
|
|
22
|
+
},
|
|
23
|
+
"engines": { "node": ">=20.0.0" }
|
|
24
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import * as readline from 'readline';
|
|
3
|
+
import { createCodingEngine } from '@backendkit-labs/agent-coding';
|
|
4
|
+
import { CallbackTransport } from '@backendkit-labs/agent-core';
|
|
5
|
+
|
|
6
|
+
// ── Provider setup ────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
const provider = process.env.LLM_PROVIDER ?? 'anthropic';
|
|
9
|
+
const apiKey = process.env.ANTHROPIC_API_KEY ?? process.env.OPENAI_API_KEY ?? process.env.LLM_API_KEY ?? '';
|
|
10
|
+
const model = process.env.LLM_MODEL;
|
|
11
|
+
|
|
12
|
+
if (!apiKey) {
|
|
13
|
+
console.error('Missing API key. Set ANTHROPIC_API_KEY or OPENAI_API_KEY in .env');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ── Engine ────────────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
const transport = new CallbackTransport((event) => {
|
|
20
|
+
if (event.type === 'token') process.stdout.write(event.content);
|
|
21
|
+
if (event.type === 'tool_call') console.log(`\n⚙ ${event.name}`);
|
|
22
|
+
if (event.type === 'block_end') console.log();
|
|
23
|
+
if (event.type === 'error') console.error(`\nError: ${event.message}`);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const engine = createCodingEngine({
|
|
27
|
+
providers: { [provider]: { apiKey, ...(model ? { model } : {}) } },
|
|
28
|
+
defaultProvider: provider,
|
|
29
|
+
workingDir: process.cwd(),
|
|
30
|
+
appName: 'my-coding-agent',
|
|
31
|
+
transport,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// ── REPL ──────────────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
37
|
+
|
|
38
|
+
function prompt() {
|
|
39
|
+
rl.question('\n> ', async (input) => {
|
|
40
|
+
const trimmed = input.trim();
|
|
41
|
+
if (!trimmed || trimmed === '/exit') { rl.close(); return; }
|
|
42
|
+
await engine.run(trimmed);
|
|
43
|
+
prompt();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log('Coding agent ready. Type your request or /exit to quit.\n');
|
|
48
|
+
prompt();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-domain-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Multi-agent system: orchestrator + domain specialists",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "ts-node src/index.ts",
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"typecheck": "tsc --noEmit"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@backendkit-labs/agent-core": "^0.14.0",
|
|
15
|
+
"dotenv": "^16.0.0",
|
|
16
|
+
"openai": "^4.0.0",
|
|
17
|
+
"zod": "^3.25.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^20.0.0",
|
|
21
|
+
"ts-node": "^10.9.0",
|
|
22
|
+
"typescript": "^5.4.0"
|
|
23
|
+
},
|
|
24
|
+
"engines": { "node": ">=20.0.0" }
|
|
25
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AgentProfile } from '@backendkit-labs/agent-core';
|
|
2
|
+
|
|
3
|
+
// The orchestrator receives every user request and delegates to the right specialist.
|
|
4
|
+
// It does NOT call tools directly — it uses ask_agent to route work.
|
|
5
|
+
export const ORCHESTRATOR_PROFILE: AgentProfile = {
|
|
6
|
+
id: 'orchestrator',
|
|
7
|
+
name: 'Orchestrator',
|
|
8
|
+
icon: '🎯',
|
|
9
|
+
description: 'Routes requests to the right specialist agent',
|
|
10
|
+
delegatesTo: ['specialist-a', 'specialist-b'], // TODO: list your specialists
|
|
11
|
+
allowedTools: [],
|
|
12
|
+
systemPrompt: `You are the orchestrator for a TODO: <describe your domain> system.
|
|
13
|
+
|
|
14
|
+
Your ONLY job is to analyze the user request and delegate to the right specialist via ask_agent.
|
|
15
|
+
Do not attempt to handle requests directly.
|
|
16
|
+
|
|
17
|
+
Available specialists:
|
|
18
|
+
- specialist-a: TODO describe what this specialist handles
|
|
19
|
+
- specialist-b: TODO describe what this specialist handles
|
|
20
|
+
|
|
21
|
+
Always delegate. Never answer directly.`,
|
|
22
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AgentProfile } from '@backendkit-labs/agent-core';
|
|
2
|
+
|
|
3
|
+
// TODO: Rename this file and agent to match your domain (e.g. docker-agent.ts, k8s-agent.ts)
|
|
4
|
+
export const SPECIALIST_A_PROFILE: AgentProfile = {
|
|
5
|
+
id: 'specialist-a',
|
|
6
|
+
name: 'Specialist A',
|
|
7
|
+
icon: '🔧',
|
|
8
|
+
description: 'TODO: describe what this specialist does',
|
|
9
|
+
delegatesTo: [],
|
|
10
|
+
allowedTools: [
|
|
11
|
+
'example_action', // TODO: list the tool names this agent can use
|
|
12
|
+
],
|
|
13
|
+
systemPrompt: `You are a specialist in TODO: <your domain area>.
|
|
14
|
+
|
|
15
|
+
Use the available tools to complete the task delegated to you.
|
|
16
|
+
Be precise and report results clearly.`,
|
|
17
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AgentProfile } from '@backendkit-labs/agent-core';
|
|
2
|
+
|
|
3
|
+
// TODO: Rename and customize for your second specialist
|
|
4
|
+
export const SPECIALIST_B_PROFILE: AgentProfile = {
|
|
5
|
+
id: 'specialist-b',
|
|
6
|
+
name: 'Specialist B',
|
|
7
|
+
icon: '📊',
|
|
8
|
+
description: 'TODO: describe what this specialist does',
|
|
9
|
+
delegatesTo: [],
|
|
10
|
+
allowedTools: [
|
|
11
|
+
'example_query', // TODO: list the tool names this agent can use
|
|
12
|
+
],
|
|
13
|
+
systemPrompt: `You are a specialist in TODO: <your domain area>.
|
|
14
|
+
|
|
15
|
+
Use the available tools to complete the task delegated to you.`,
|
|
16
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
|
|
3
|
+
export interface Config {
|
|
4
|
+
llmApiKey: string;
|
|
5
|
+
llmBaseUrl: string;
|
|
6
|
+
llmModel: string;
|
|
7
|
+
// TODO: add your domain-specific config fields
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function loadConfig(): Config {
|
|
11
|
+
const llmApiKey = process.env.LLM_API_KEY ?? '';
|
|
12
|
+
if (!llmApiKey) throw new Error('Missing LLM_API_KEY in environment');
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
llmApiKey,
|
|
16
|
+
llmBaseUrl: process.env.LLM_BASE_URL ?? 'https://api.openai.com/v1',
|
|
17
|
+
llmModel: process.env.LLM_MODEL ?? 'gpt-4o',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentEngine,
|
|
3
|
+
AgentRegistry,
|
|
4
|
+
ToolRegistry,
|
|
5
|
+
ProviderRegistry,
|
|
6
|
+
CallbackTransport,
|
|
7
|
+
} from '@backendkit-labs/agent-core';
|
|
8
|
+
import { OpenAICompatibleProvider } from './providers/openai-compatible';
|
|
9
|
+
import { loadConfig } from './config';
|
|
10
|
+
import { ORCHESTRATOR_PROFILE } from './agents/orchestrator';
|
|
11
|
+
import { SPECIALIST_A_PROFILE } from './agents/specialist-a';
|
|
12
|
+
import { SPECIALIST_B_PROFILE } from './agents/specialist-b';
|
|
13
|
+
import { exampleAction, exampleQuery } from './tools/example-tools';
|
|
14
|
+
|
|
15
|
+
export function createDomainEngine(transport: CallbackTransport): AgentEngine {
|
|
16
|
+
const config = loadConfig();
|
|
17
|
+
|
|
18
|
+
const tools = new ToolRegistry();
|
|
19
|
+
tools
|
|
20
|
+
.register(exampleAction)
|
|
21
|
+
.register(exampleQuery);
|
|
22
|
+
// TODO: register more tools
|
|
23
|
+
|
|
24
|
+
const agents = new AgentRegistry();
|
|
25
|
+
agents
|
|
26
|
+
.register(ORCHESTRATOR_PROFILE)
|
|
27
|
+
.register(SPECIALIST_A_PROFILE)
|
|
28
|
+
.register(SPECIALIST_B_PROFILE);
|
|
29
|
+
// TODO: register more specialists
|
|
30
|
+
|
|
31
|
+
const providers = new ProviderRegistry();
|
|
32
|
+
providers.register('default', new OpenAICompatibleProvider({
|
|
33
|
+
apiKey: config.llmApiKey,
|
|
34
|
+
baseUrl: config.llmBaseUrl,
|
|
35
|
+
model: config.llmModel,
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
return new AgentEngine({
|
|
39
|
+
model: { provider: 'default', id: config.llmModel },
|
|
40
|
+
agents,
|
|
41
|
+
tools,
|
|
42
|
+
providers,
|
|
43
|
+
defaultProvider: 'default',
|
|
44
|
+
defaultAgentId: 'orchestrator',
|
|
45
|
+
transport,
|
|
46
|
+
maxIterations: 30,
|
|
47
|
+
iterationMode: 'auto',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
import { CallbackTransport } from '@backendkit-labs/agent-core';
|
|
3
|
+
import { createDomainEngine } from './engine';
|
|
4
|
+
|
|
5
|
+
const transport = new CallbackTransport((event) => {
|
|
6
|
+
if (event.type === 'token') process.stdout.write(event.content);
|
|
7
|
+
if (event.type === 'tool_call') console.log(`\n⚙ ${event.name}`);
|
|
8
|
+
if (event.type === 'agent_switch') console.log(`\n→ ${event.to_name ?? event.to}`);
|
|
9
|
+
if (event.type === 'block_end') console.log();
|
|
10
|
+
if (event.type === 'error') console.error(`\nError: ${event.message}`);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const engine = createDomainEngine(transport);
|
|
14
|
+
|
|
15
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
16
|
+
|
|
17
|
+
function prompt() {
|
|
18
|
+
rl.question('\n> ', async (input) => {
|
|
19
|
+
const trimmed = input.trim();
|
|
20
|
+
if (!trimmed || trimmed === '/exit') { rl.close(); return; }
|
|
21
|
+
await engine.run(trimmed);
|
|
22
|
+
prompt();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log('Domain agent ready. Type your request or /exit to quit.\n');
|
|
27
|
+
prompt();
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import type { LLMProvider, LLMMessage, LLMStreamCallbacks, ToolDefinition } from '@backendkit-labs/agent-core';
|
|
3
|
+
|
|
4
|
+
export interface OpenAICompatibleProviderOptions {
|
|
5
|
+
apiKey: string;
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
model: string;
|
|
8
|
+
maxRetries?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isRetryable(err: unknown): boolean {
|
|
12
|
+
if (err && typeof err === 'object' && 'status' in err) {
|
|
13
|
+
const s = (err as { status: number }).status;
|
|
14
|
+
return s === 429 || s >= 500;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const sleep = (ms: number) => new Promise<void>(r => setTimeout(r, ms));
|
|
20
|
+
|
|
21
|
+
export class OpenAICompatibleProvider implements LLMProvider {
|
|
22
|
+
private readonly client: OpenAI;
|
|
23
|
+
private readonly model: string;
|
|
24
|
+
private readonly maxRetries: number;
|
|
25
|
+
|
|
26
|
+
constructor(opts: OpenAICompatibleProviderOptions) {
|
|
27
|
+
this.client = new OpenAI({ apiKey: opts.apiKey, baseURL: opts.baseUrl, maxRetries: 0 });
|
|
28
|
+
this.model = opts.model;
|
|
29
|
+
this.maxRetries = opts.maxRetries ?? 3;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async chat(messages: LLMMessage[], tools: ToolDefinition[], callbacks: LLMStreamCallbacks): Promise<void> {
|
|
33
|
+
const oaiTools = tools.map(t => ({
|
|
34
|
+
type: 'function' as const,
|
|
35
|
+
function: { name: t.name, description: t.description, parameters: t.parameters },
|
|
36
|
+
})) as OpenAI.Chat.ChatCompletionTool[];
|
|
37
|
+
|
|
38
|
+
let attempt = 0;
|
|
39
|
+
let emitted = false;
|
|
40
|
+
|
|
41
|
+
while (attempt < this.maxRetries) {
|
|
42
|
+
attempt++;
|
|
43
|
+
try {
|
|
44
|
+
await this.stream(messages, oaiTools, {
|
|
45
|
+
...callbacks,
|
|
46
|
+
onChunk: (d) => { emitted = true; callbacks.onChunk?.(d); },
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
if (emitted || !isRetryable(err) || attempt >= this.maxRetries) {
|
|
51
|
+
callbacks.onError(err instanceof Error ? err : new Error(String(err)));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
await sleep(Math.min(1000 * 2 ** attempt, 16_000));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private async stream(
|
|
60
|
+
messages: LLMMessage[],
|
|
61
|
+
oaiTools: OpenAI.Chat.ChatCompletionTool[],
|
|
62
|
+
callbacks: LLMStreamCallbacks,
|
|
63
|
+
): Promise<void> {
|
|
64
|
+
const stream = await this.client.chat.completions.create({
|
|
65
|
+
model: this.model,
|
|
66
|
+
messages: messages as OpenAI.Chat.ChatCompletionMessageParam[],
|
|
67
|
+
tools: oaiTools.length ? oaiTools : undefined,
|
|
68
|
+
tool_choice: oaiTools.length ? 'auto' : undefined,
|
|
69
|
+
stream: true,
|
|
70
|
+
stream_options: { include_usage: true },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
let content = '';
|
|
74
|
+
const toolBuffers = new Map<number, { id: string; name: string; args: string }>();
|
|
75
|
+
|
|
76
|
+
for await (const chunk of stream) {
|
|
77
|
+
if (chunk.usage) callbacks.onMetrics?.(chunk.usage.prompt_tokens, chunk.usage.completion_tokens);
|
|
78
|
+
const delta = chunk.choices[0]?.delta;
|
|
79
|
+
if (!delta) continue;
|
|
80
|
+
if (delta.content) { content += delta.content; callbacks.onChunk?.(delta.content); }
|
|
81
|
+
if (delta.tool_calls) {
|
|
82
|
+
for (const tc of delta.tool_calls) {
|
|
83
|
+
if (!toolBuffers.has(tc.index)) toolBuffers.set(tc.index, { id: '', name: '', args: '' });
|
|
84
|
+
const buf = toolBuffers.get(tc.index)!;
|
|
85
|
+
if (tc.id) buf.id = tc.id;
|
|
86
|
+
if (tc.function?.name) buf.name += tc.function.name;
|
|
87
|
+
if (tc.function?.arguments) buf.args += tc.function.arguments;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const toolCalls = [...toolBuffers.entries()]
|
|
93
|
+
.sort(([a], [b]) => a - b)
|
|
94
|
+
.map(([, b]) => ({ id: b.id, type: 'function' as const, function: { name: b.name, arguments: b.args } }));
|
|
95
|
+
|
|
96
|
+
for (const tc of toolCalls) callbacks.onToolCall?.(tc.function.name, tc.function.arguments, tc.id);
|
|
97
|
+
|
|
98
|
+
callbacks.onDone({
|
|
99
|
+
role: 'assistant',
|
|
100
|
+
content: content || null,
|
|
101
|
+
tool_calls: toolCalls.length ? toolCalls : undefined,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineTool, z } from '@backendkit-labs/agent-core';
|
|
2
|
+
|
|
3
|
+
// TODO: Replace these with tools that call your actual domain SDK/API
|
|
4
|
+
|
|
5
|
+
export const exampleAction = defineTool({
|
|
6
|
+
name: 'example_action',
|
|
7
|
+
description: 'TODO: describe what this tool does',
|
|
8
|
+
input: z.object({
|
|
9
|
+
target: z.string().describe('Target to act on'),
|
|
10
|
+
// TODO: add more parameters
|
|
11
|
+
}),
|
|
12
|
+
execute: async ({ target }) => {
|
|
13
|
+
// TODO: implement the actual action
|
|
14
|
+
return `Action executed on: ${target}`;
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const exampleQuery = defineTool({
|
|
19
|
+
name: 'example_query',
|
|
20
|
+
description: 'TODO: describe what this query returns',
|
|
21
|
+
input: z.object({
|
|
22
|
+
filter: z.string().optional().describe('Optional filter'),
|
|
23
|
+
}),
|
|
24
|
+
execute: async ({ filter }) => {
|
|
25
|
+
// TODO: implement the actual query
|
|
26
|
+
return JSON.stringify({ results: [], filter });
|
|
27
|
+
},
|
|
28
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "MCP server — expose agent tools via Model Context Protocol",
|
|
6
|
+
"main": "dist/server.js",
|
|
7
|
+
"bin": { "my-mcp-server": "./dist/server.js" },
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "ts-node src/server.ts",
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "node dist/server.js",
|
|
12
|
+
"typecheck": "tsc --noEmit"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@backendkit-labs/agent-core": "^0.14.0",
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
17
|
+
"dotenv": "^16.0.0",
|
|
18
|
+
"zod": "^3.25.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.0.0",
|
|
22
|
+
"ts-node": "^10.9.0",
|
|
23
|
+
"typescript": "^5.4.0"
|
|
24
|
+
},
|
|
25
|
+
"engines": { "node": ">=20.0.0" }
|
|
26
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import 'dotenv/config';
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { exampleHandler, exampleInputSchema } from './tools/example';
|
|
7
|
+
|
|
8
|
+
// ── MCP Server ────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
const server = new McpServer({
|
|
11
|
+
name: 'my-mcp-server',
|
|
12
|
+
version: '0.1.0',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// ── Register tools ────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
// @ts-expect-error: MCP SDK + zod generic depth limit, safe at runtime
|
|
18
|
+
server.tool(
|
|
19
|
+
'example_query',
|
|
20
|
+
'TODO: describe what this tool does',
|
|
21
|
+
exampleInputSchema.shape,
|
|
22
|
+
async (args: z.infer<typeof exampleInputSchema>) => ({
|
|
23
|
+
content: [{ type: 'text' as const, text: await exampleHandler(args) }],
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// TODO: register more tools following the same pattern:
|
|
28
|
+
// server.tool('tool_name', 'description', schema.shape, handler)
|
|
29
|
+
|
|
30
|
+
// ── Transport: stdio (for Claude Desktop / Cursor / VS Code) ─────────────────
|
|
31
|
+
|
|
32
|
+
const transport = new StdioServerTransport();
|
|
33
|
+
await server.connect(transport);
|
|
34
|
+
|
|
35
|
+
// To use this server, add it to your MCP client config:
|
|
36
|
+
// {
|
|
37
|
+
// "mcpServers": {
|
|
38
|
+
// "my-server": { "command": "npx", "args": ["my-mcp-server"] }
|
|
39
|
+
// }
|
|
40
|
+
// }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const exampleInputSchema = z.object({
|
|
4
|
+
query: z.string().describe('The query to process'),
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
export async function exampleHandler({ query }: z.infer<typeof exampleInputSchema>): Promise<string> {
|
|
8
|
+
// TODO: implement your tool logic
|
|
9
|
+
return `Result for: ${query}`;
|
|
10
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"esModuleInterop": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"],
|
|
14
|
+
"exclude": ["node_modules", "dist"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Minimal agent — full control over engine configuration",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "ts-node src/index.ts",
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"typecheck": "tsc --noEmit"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@backendkit-labs/agent-core": "^0.14.0",
|
|
15
|
+
"dotenv": "^16.0.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^20.0.0",
|
|
19
|
+
"ts-node": "^10.9.0",
|
|
20
|
+
"typescript": "^5.4.0"
|
|
21
|
+
},
|
|
22
|
+
"engines": { "node": ">=20.0.0" }
|
|
23
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { createBaseEngine } from '@backendkit-labs/agent-core';
|
|
3
|
+
import type { AgentProfile, LLMProvider } from '@backendkit-labs/agent-core';
|
|
4
|
+
|
|
5
|
+
// ── Provider ──────────────────────────────────────────────────────────────────
|
|
6
|
+
// TODO: replace with the actual provider for your chosen LLM
|
|
7
|
+
// e.g. AnthropicProvider from @backendkit-labs/agent-coding
|
|
8
|
+
// or implement LLMProvider interface directly
|
|
9
|
+
|
|
10
|
+
const myProvider: LLMProvider = {
|
|
11
|
+
chat: async (_messages, _tools, callbacks) => {
|
|
12
|
+
// TODO: call your LLM API here
|
|
13
|
+
callbacks.onDone({ role: 'assistant', content: 'Hello from my agent!' });
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// ── Agent ─────────────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
const myAgent: AgentProfile = {
|
|
20
|
+
id: 'my-agent',
|
|
21
|
+
name: 'My Agent',
|
|
22
|
+
description: 'TODO: describe your agent',
|
|
23
|
+
allowedTools: [],
|
|
24
|
+
delegatesTo: [],
|
|
25
|
+
systemPrompt: 'TODO: write your system prompt here.',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// ── Engine ────────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
const engine = createBaseEngine({
|
|
31
|
+
providers: { default: myProvider },
|
|
32
|
+
defaultProvider: 'default',
|
|
33
|
+
agents: [myAgent],
|
|
34
|
+
defaultAgent: 'my-agent',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// ── Run ───────────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
const input = process.argv[2] ?? 'Hello';
|
|
40
|
+
await engine.run(input);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"esModuleInterop": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"],
|
|
14
|
+
"exclude": ["node_modules", "dist"]
|
|
15
|
+
}
|