@donkeylabs/mcp 0.4.2 → 0.4.3
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/package.json +1 -1
- package/src/server.ts +83 -48
package/package.json
CHANGED
package/src/server.ts
CHANGED
|
@@ -282,6 +282,13 @@ function findDocsDir(): string {
|
|
|
282
282
|
const DOCS_DIR = findDocsDir();
|
|
283
283
|
|
|
284
284
|
const RESOURCES = [
|
|
285
|
+
{
|
|
286
|
+
uri: "donkeylabs://docs/database",
|
|
287
|
+
name: "Database (Kysely)",
|
|
288
|
+
description: "Kysely queries, CRUD operations, joins, transactions, migrations",
|
|
289
|
+
mimeType: "text/markdown",
|
|
290
|
+
docFile: "database.md",
|
|
291
|
+
},
|
|
285
292
|
{
|
|
286
293
|
uri: "donkeylabs://docs/project-structure",
|
|
287
294
|
name: "Project Structure",
|
|
@@ -939,6 +946,25 @@ async function getArchitectureGuidance(args: { task: string }): Promise<string>
|
|
|
939
946
|
|
|
940
947
|
let guidance = `# Architecture Guidance\n\n**Task:** ${task}\n\n`;
|
|
941
948
|
|
|
949
|
+
// Always include the Plugin vs Route decision guidance
|
|
950
|
+
guidance += `## When to Create a Plugin vs Route\n\n`;
|
|
951
|
+
guidance += `**Core Principle:** Plugins = Reusable Business Logic | Routes = App-Specific API Endpoints\n\n`;
|
|
952
|
+
guidance += `### Create a Plugin when:\n`;
|
|
953
|
+
guidance += `- The logic could be **reused** across multiple routes or apps (auth, email, payments)\n`;
|
|
954
|
+
guidance += `- You need **database tables** for a domain concept (users, orders, products)\n`;
|
|
955
|
+
guidance += `- The functionality is **self-contained** with its own data and operations\n`;
|
|
956
|
+
guidance += `- You're building **cross-cutting concerns** like middleware\n\n`;
|
|
957
|
+
guidance += `### Create a Route when:\n`;
|
|
958
|
+
guidance += `- **Exposing plugin functionality** via HTTP (thin wrapper calling plugin methods)\n`;
|
|
959
|
+
guidance += `- **Combining multiple plugins** for a specific use case\n`;
|
|
960
|
+
guidance += `- Building **app-specific endpoints** that don't need reuse\n`;
|
|
961
|
+
guidance += `- **Simple operations** that don't warrant a full plugin\n\n`;
|
|
962
|
+
guidance += `### Workflow:\n`;
|
|
963
|
+
guidance += `1. Identify reusable domains → Create plugins with business logic\n`;
|
|
964
|
+
guidance += `2. Create routes that use plugins to expose functionality\n`;
|
|
965
|
+
guidance += `3. Keep routes thin - delegate to plugin service methods\n\n`;
|
|
966
|
+
guidance += `---\n\n`;
|
|
967
|
+
|
|
942
968
|
// Pattern matching for common tasks
|
|
943
969
|
if (taskLower.includes("auth") || taskLower.includes("login") || taskLower.includes("user")) {
|
|
944
970
|
guidance += `## Recommended Approach: Authentication System\n\n`;
|
|
@@ -1073,10 +1099,11 @@ async function getArchitectureGuidance(args: { task: string }): Promise<string>
|
|
|
1073
1099
|
}
|
|
1074
1100
|
|
|
1075
1101
|
guidance += `\n## Documentation Resources\n`;
|
|
1076
|
-
guidance += `- \`donkeylabs://docs/
|
|
1077
|
-
guidance += `- \`donkeylabs://docs/
|
|
1078
|
-
guidance += `- \`donkeylabs://docs/
|
|
1079
|
-
guidance += `- \`donkeylabs://docs/
|
|
1102
|
+
guidance += `- \`donkeylabs://docs/database\` - Kysely queries, CRUD, joins, transactions\n`;
|
|
1103
|
+
guidance += `- \`donkeylabs://docs/plugins\` - Plugin patterns & When to Create a Plugin vs Route\n`;
|
|
1104
|
+
guidance += `- \`donkeylabs://docs/router\` - Route patterns & best practices\n`;
|
|
1105
|
+
guidance += `- \`donkeylabs://docs/api-client\` - Generated client usage\n`;
|
|
1106
|
+
guidance += `- \`donkeylabs://docs/handlers\` - Class-based handlers\n`;
|
|
1080
1107
|
|
|
1081
1108
|
return guidance;
|
|
1082
1109
|
}
|
|
@@ -1153,7 +1180,9 @@ async function createPlugin(args: {
|
|
|
1153
1180
|
// Build the createPlugin chain
|
|
1154
1181
|
let createPluginChain = "createPlugin";
|
|
1155
1182
|
if (hasSchema) {
|
|
1156
|
-
|
|
1183
|
+
// Note: DB type is auto-generated by kysely-codegen from migrations
|
|
1184
|
+
// Use {} until `donkeylabs generate` creates schema.ts, then change to DB
|
|
1185
|
+
createPluginChain += `\n .withSchema<{}>() // Change to <DB> after running \`donkeylabs generate\``;
|
|
1157
1186
|
}
|
|
1158
1187
|
if (hasConfig) {
|
|
1159
1188
|
createPluginChain += `\n .withConfig<${toPascalCase(name)}Config>()`;
|
|
@@ -1162,11 +1191,14 @@ async function createPlugin(args: {
|
|
|
1162
1191
|
|
|
1163
1192
|
// Generate imports
|
|
1164
1193
|
let imports = `import { createPlugin } from "@donkeylabs/server";\n`;
|
|
1194
|
+
imports += `import { z } from "zod";\n`;
|
|
1165
1195
|
if (dependencies.length > 0) {
|
|
1166
1196
|
imports += dependencies.map(d => `import { ${d}Plugin } from "../${d}";`).join("\n") + "\n";
|
|
1167
1197
|
}
|
|
1168
1198
|
if (hasSchema) {
|
|
1169
|
-
|
|
1199
|
+
// Note: schema.ts is auto-generated after running `donkeylabs generate`
|
|
1200
|
+
// Once generated, uncomment this import:
|
|
1201
|
+
imports += `// import type { DB } from "./schema"; // Uncomment after running \`donkeylabs generate\`\n`;
|
|
1170
1202
|
}
|
|
1171
1203
|
|
|
1172
1204
|
// Generate config interface if needed
|
|
@@ -1224,43 +1256,36 @@ ${ctxComment}
|
|
|
1224
1256
|
|
|
1225
1257
|
await Bun.write(join(pluginDir, "index.ts"), indexContent);
|
|
1226
1258
|
|
|
1227
|
-
// Create
|
|
1259
|
+
// Create migrations folder if plugin has schema
|
|
1260
|
+
// Note: schema.ts is auto-generated by `donkeylabs generate` from migrations
|
|
1228
1261
|
if (hasSchema) {
|
|
1229
1262
|
mkdirSync(join(pluginDir, "migrations"), { recursive: true });
|
|
1230
1263
|
|
|
1231
|
-
const
|
|
1232
|
-
// Run 'donkeylabs generate' after adding migrations to generate types
|
|
1264
|
+
const migrationContent = `import { Kysely, sql } from "kysely";
|
|
1233
1265
|
|
|
1234
|
-
|
|
1235
|
-
|
|
1266
|
+
/**
|
|
1267
|
+
* Migration: 001_initial
|
|
1268
|
+
* Created: ${new Date().toISOString()}
|
|
1269
|
+
* Plugin: ${name}
|
|
1270
|
+
*/
|
|
1271
|
+
|
|
1272
|
+
export async function up(db: Kysely<any>): Promise<void> {
|
|
1273
|
+
// Create your tables here
|
|
1236
1274
|
// Example:
|
|
1237
|
-
//
|
|
1238
|
-
//
|
|
1239
|
-
//
|
|
1240
|
-
//
|
|
1241
|
-
//
|
|
1275
|
+
// await db.schema
|
|
1276
|
+
// .createTable("${name}")
|
|
1277
|
+
// .addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
1278
|
+
// .addColumn("name", "text", (col) => col.notNull())
|
|
1279
|
+
// .addColumn("created_at", "text", (col) => col.defaultTo(sql\`CURRENT_TIMESTAMP\`))
|
|
1280
|
+
// .execute();
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
export async function down(db: Kysely<any>): Promise<void> {
|
|
1284
|
+
// Drop your tables here
|
|
1285
|
+
// await db.schema.dropTable("${name}").execute();
|
|
1242
1286
|
}
|
|
1243
1287
|
`;
|
|
1244
|
-
await Bun.write(join(pluginDir, "
|
|
1245
|
-
|
|
1246
|
-
const migrationContent = `-- Migration: 001_initial
|
|
1247
|
-
-- Created: ${new Date().toISOString()}
|
|
1248
|
-
-- Plugin: ${name}
|
|
1249
|
-
|
|
1250
|
-
-- UP
|
|
1251
|
-
-- Add your CREATE TABLE statements here
|
|
1252
|
-
-- Example:
|
|
1253
|
-
-- CREATE TABLE ${name} (
|
|
1254
|
-
-- id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1255
|
-
-- name TEXT NOT NULL,
|
|
1256
|
-
-- created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
1257
|
-
-- );
|
|
1258
|
-
|
|
1259
|
-
-- DOWN
|
|
1260
|
-
-- Add your DROP TABLE statements here
|
|
1261
|
-
-- DROP TABLE IF EXISTS ${name};
|
|
1262
|
-
`;
|
|
1263
|
-
await Bun.write(join(pluginDir, "migrations", "001_initial.sql"), migrationContent);
|
|
1288
|
+
await Bun.write(join(pluginDir, "migrations", "001_initial.ts"), migrationContent);
|
|
1264
1289
|
}
|
|
1265
1290
|
|
|
1266
1291
|
// Build registration example based on plugin type
|
|
@@ -1401,25 +1426,35 @@ async function addMigration(args: {
|
|
|
1401
1426
|
mkdirSync(migrationsDir, { recursive: true });
|
|
1402
1427
|
}
|
|
1403
1428
|
|
|
1404
|
-
// Find next migration number
|
|
1429
|
+
// Find next migration number - check both .ts and .sql files for backwards compatibility
|
|
1405
1430
|
const existing = readdirSync(migrationsDir)
|
|
1406
|
-
.filter((f) => f.endsWith(".sql"))
|
|
1431
|
+
.filter((f) => f.endsWith(".ts") || f.endsWith(".sql"))
|
|
1407
1432
|
.map((f) => parseInt(f.split("_")[0], 10))
|
|
1408
1433
|
.filter((n) => !isNaN(n));
|
|
1409
1434
|
|
|
1410
1435
|
const nextNum = existing.length > 0 ? Math.max(...existing) + 1 : 1;
|
|
1411
1436
|
const numStr = String(nextNum).padStart(3, "0");
|
|
1412
|
-
const filename = `${numStr}_${migrationName}.
|
|
1437
|
+
const filename = `${numStr}_${migrationName}.ts`;
|
|
1438
|
+
|
|
1439
|
+
// Escape backticks in SQL for template literal
|
|
1440
|
+
const escapedUpSql = upSql.replace(/`/g, "\\`");
|
|
1441
|
+
const escapedDownSql = (downSql || "").replace(/`/g, "\\`");
|
|
1442
|
+
|
|
1443
|
+
const content = `import { Kysely, sql } from "kysely";
|
|
1413
1444
|
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1445
|
+
/**
|
|
1446
|
+
* Migration: ${numStr}_${migrationName}
|
|
1447
|
+
* Created: ${new Date().toISOString()}
|
|
1448
|
+
* Plugin: ${pluginName}
|
|
1449
|
+
*/
|
|
1417
1450
|
|
|
1418
|
-
|
|
1419
|
-
|
|
1451
|
+
export async function up(db: Kysely<any>): Promise<void> {
|
|
1452
|
+
await sql\`${escapedUpSql}\`.execute(db);
|
|
1453
|
+
}
|
|
1420
1454
|
|
|
1421
|
-
|
|
1422
|
-
${
|
|
1455
|
+
export async function down(db: Kysely<any>): Promise<void> {
|
|
1456
|
+
${escapedDownSql ? `await sql\`${escapedDownSql}\`.execute(db);` : "// Add rollback logic here"}
|
|
1457
|
+
}
|
|
1423
1458
|
`;
|
|
1424
1459
|
|
|
1425
1460
|
await Bun.write(join(migrationsDir, filename), content);
|
|
@@ -1431,7 +1466,7 @@ ${downSql || "-- Add rollback SQL here"}
|
|
|
1431
1466
|
|
|
1432
1467
|
### Next Steps
|
|
1433
1468
|
|
|
1434
|
-
1. Review the migration
|
|
1469
|
+
1. Review the migration
|
|
1435
1470
|
2. Run \`donkeylabs generate\` to update schema types
|
|
1436
1471
|
3. The migration will run automatically on server start
|
|
1437
1472
|
|
|
@@ -1589,7 +1624,7 @@ export class ${handlerClassName} implements Handler<Routes.${prefix}.${handlerNa
|
|
|
1589
1624
|
this.ctx = ctx;
|
|
1590
1625
|
}
|
|
1591
1626
|
|
|
1592
|
-
async handle(input: Routes.${prefix}.${handlerName}
|
|
1627
|
+
async handle(input: Routes.${prefix}.${handlerName}.Input): Promise<Routes.${prefix}.${handlerName}.Output> {
|
|
1593
1628
|
${handler}
|
|
1594
1629
|
}
|
|
1595
1630
|
}
|