Skills are SKILL.md files the agent loads on demand. Built-in skills ship with Dodo; personal skills you author here. Workspace skills (per-session, from .dodo/skills/ etc.) are not shown here.
-
-
-
-
Tools
-
Tools available to the agent. Dimmed entries are gated on session config (codemode, browser) or reachable only inside a codemode block (most git ops). Configure MCP integrations below to add more.
-
-
Integrations
@@ -934,6 +924,20 @@
Integrations
+
+
+
Skills
+
Skills are SKILL.md files the agent loads on demand. Built-in skills ship with Dodo; personal skills you author here. Workspace skills (per-session, from .dodo/skills/ etc.) are not shown here.
+
+
+
+
+
+
Tools
+
Tools available to the agent. Dimmed entries are gated on session config (codemode, browser) or reachable only inside a codemode block (most git ops). Configure MCP integrations above to add more.
+
+
+
Secrets
diff --git a/src/user-control.ts b/src/user-control.ts
index fb8ca3d..567ed9a 100644
--- a/src/user-control.ts
+++ b/src/user-control.ts
@@ -1468,25 +1468,29 @@ export class UserControl extends DurableObject {
this.ctx.storage.sql.exec("INSERT OR IGNORE INTO user_config (key, value, updated_at) VALUES (?, ?, ?)", key, String(value), now);
}
- const existingCount = (Array.from(this.ctx.storage.sql.exec("SELECT COUNT(*) AS n FROM approved_mcps WHERE is_deleted = 0"))[0] as SqlRow | null);
- if (Number(existingCount?.n ?? 0) === 0) {
- const { getDeployMcpCatalog } = await import("./mcp-catalog");
- const seedNow = Date.now();
- for (const entry of getDeployMcpCatalog(this.env)) {
- this.ctx.storage.sql.exec(
- `INSERT INTO approved_mcps (mcp_url, id, display_name, description, setup_guide, known_hosts, auth_type, status, is_deleted, created_at, updated_at)
- VALUES (?, ?, ?, ?, ?, ?, ?, 'enabled', 0, ?, ?)`,
- entry.url,
- entry.id,
- entry.name,
- entry.description ?? null,
- entry.setupGuide ?? null,
- JSON.stringify(entry.knownHosts ?? []),
- entry.auth_type ?? "static_headers",
- seedNow,
- seedNow,
- );
- }
+ // Seed `approved_mcps` from the code-level catalog. We use `INSERT OR IGNORE`
+ // keyed by `mcp_url` so:
+ // - On first run, every catalog entry is inserted.
+ // - On subsequent runs, only NEW entries (not yet in the table) are
+ // inserted; admin-edited rows and soft-deleted rows are preserved.
+ // This is how adding a new entry to `mcp-catalog.ts` propagates to all
+ // existing UserControl DOs without an explicit migration.
+ const { getDeployMcpCatalog } = await import("./mcp-catalog");
+ const seedNow = Date.now();
+ for (const entry of getDeployMcpCatalog(this.env)) {
+ this.ctx.storage.sql.exec(
+ `INSERT OR IGNORE INTO approved_mcps (mcp_url, id, display_name, description, setup_guide, known_hosts, auth_type, status, is_deleted, created_at, updated_at)
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'enabled', 0, ?, ?)`,
+ entry.url,
+ entry.id,
+ entry.name,
+ entry.description ?? null,
+ entry.setupGuide ?? null,
+ JSON.stringify(entry.knownHosts ?? []),
+ entry.auth_type ?? "static_headers",
+ seedNow,
+ seedNow,
+ );
}
}