Cli to dev (#309)

* add workspace access guidelines to instructions

* updated example

* removed incorrect example

* add --example to add the examples from rowboat

* changed --example to --sync-example

* rename sync-examples option to sync-example in CLI

* fix: sync-example implementation

* refactor example import

* fix yargs

* fix: - remove changes to package-lock
- remove output messages from app.js and moved them into importExample

* fix: restore package-lock.json to match main (remove diff)

* fix: naming of the commands

* update: made import-example into import and it can import example workflows or user made workflows

* update: added export capability

* delete: remove misplaced podcast.json file

* removed incomplete gemini3-test example json

* remove: eliminate gemini3-test example from exports

* Fix: better prompting around MCP config
Add: copilot tool to add MCP servers

* clean up prompt

---------

Co-authored-by: Ramnique Singh <30795890+ramnique@users.noreply.github.com>
This commit is contained in:
Tushar 2025-11-25 20:00:43 +05:30 committed by Ramnique Singh
parent 255bc9c48d
commit e47518b98f
12 changed files with 1420 additions and 171 deletions

View file

@ -27,17 +27,27 @@ Always consult this catalog first so you load the right skills before taking act
- Use relative paths (no \${BASE_DIR} prefixes) when running commands or referencing files.
- Keep user data safedouble-check before editing or deleting important resources.
## Workspace access & scope
- You have full read/write access inside \`${BASE_DIR}\` (this resolves to the user's \`~/.rowboat\` directory). Create folders, files, and agents there using builtin tools or allowed shell commands—don't wait for the user to do it manually.
- If a user mentions a different root (e.g., \`~/.rowboatx\` or another path), clarify whether they meant the Rowboat workspace and propose the equivalent path you can act on. Only refuse if they explicitly insist on an inaccessible location.
- Prefer builtin file tools (\`createFile\`, \`updateFile\`, \`deleteFile\`, \`exploreDirectory\`) for workspace changes. Reserve refusal or "you do it" responses for cases that are truly outside the Rowboat sandbox.
## Builtin Tools vs Shell Commands
**IMPORTANT**: Rowboat provides builtin tools that are internal and do NOT require security allowlist entries:
- \`deleteFile\`, \`createFile\`, \`updateFile\`, \`readFile\` - File operations
- \`listFiles\`, \`exploreDirectory\` - Directory exploration
- \`analyzeAgent\` - Agent analysis
- \`listMcpServers\`, \`listMcpTools\` - MCP server management
- \`addMcpServer\`, \`listMcpServers\`, \`listMcpTools\` - MCP server management
- \`loadSkill\` - Skill loading
These tools work directly and are NOT filtered by \`.rowboat/config/security.json\`.
**CRITICAL: MCP Server Configuration**
- ALWAYS use the \`addMcpServer\` builtin tool to add or update MCP servers—it validates the configuration before saving
- NEVER manually edit \`config/mcp.json\` using \`createFile\` or \`updateFile\` for MCP servers
- Invalid MCP configs will prevent the agent from starting with validation errors
**Only \`executeCommand\` (shell/bash commands) is filtered** by the security allowlist. If you need to delete a file, use the \`deleteFile\` builtin tool, not \`executeCommand\` with \`rm\`. If you need to create a file, use \`createFile\`, not \`executeCommand\` with \`touch\` or \`echo >\`.
The security allowlist in \`security.json\` only applies to shell commands executed via \`executeCommand\`, not to Rowboat's internal builtin tools.

View file

@ -1,22 +1,225 @@
export const skill = String.raw`
# MCP Integration Guidance
Load this skill whenever a user asks about external tools, MCP servers, or how to extend an agents capabilities.
Load this skill whenever a user asks about external tools, MCP servers, or how to extend an agent's capabilities.
## Key concepts
- MCP servers expose tools (web scraping, APIs, databases, etc.) declared in \`config/mcp.json\`.
- Agents reference MCP tools through the \`"tools"\` block by specifying \`type\`, \`name\`, \`description\`, \`mcpServerName\`, and a full \`inputSchema\`.
- Tool schemas can include optional property descriptions; only include \`"required"\` when parameters are mandatory.
## CRITICAL: Adding MCP Servers
**ALWAYS use the \`addMcpServer\` builtin tool** to add or update MCP server configurations. This tool validates the configuration before saving and prevents startup errors.
**NEVER manually create or edit \`config/mcp.json\`** using \`createFile\` or \`updateFile\` for MCP servers—this bypasses validation and will cause errors.
### MCP Server Configuration Schema
There are TWO types of MCP servers:
#### 1. STDIO (Command-based) Servers
For servers that run as local processes (Node.js, Python, etc.):
**Required fields:**
- \`command\`: string (e.g., "npx", "node", "python", "uvx")
**Optional fields:**
- \`args\`: array of strings (command arguments)
- \`env\`: object with string key-value pairs (environment variables)
- \`type\`: "stdio" (optional, inferred from presence of \`command\`)
**Schema:**
\`\`\`json
{
"type": "stdio",
"command": "string (REQUIRED)",
"args": ["string", "..."],
"env": {
"KEY": "value"
}
}
\`\`\`
**Valid STDIO examples:**
\`\`\`json
{
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/data"]
}
\`\`\`
\`\`\`json
{
"command": "python",
"args": ["-m", "mcp_server_git"],
"env": {
"GIT_REPO_PATH": "/path/to/repo"
}
}
\`\`\`
\`\`\`json
{
"command": "uvx",
"args": ["mcp-server-fetch"]
}
\`\`\`
#### 2. HTTP/SSE Servers
For servers that expose HTTP or Server-Sent Events endpoints:
**Required fields:**
- \`url\`: string (complete URL including protocol and path)
**Optional fields:**
- \`headers\`: object with string key-value pairs (HTTP headers)
- \`type\`: "http" (optional, inferred from presence of \`url\`)
**Schema:**
\`\`\`json
{
"type": "http",
"url": "string (REQUIRED)",
"headers": {
"Authorization": "Bearer token",
"Custom-Header": "value"
}
}
\`\`\`
**Valid HTTP examples:**
\`\`\`json
{
"url": "http://localhost:3000/sse"
}
\`\`\`
\`\`\`json
{
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer sk-1234567890"
}
}
\`\`\`
### Common Validation Errors to Avoid
**WRONG - Missing required field:**
\`\`\`json
{
"args": ["some-arg"]
}
\`\`\`
Error: Missing \`command\` for stdio OR \`url\` for http
**WRONG - Empty object:**
\`\`\`json
{}
\`\`\`
Error: Must have either \`command\` (stdio) or \`url\` (http)
**WRONG - Mixed types:**
\`\`\`json
{
"command": "npx",
"url": "http://localhost:3000"
}
\`\`\`
Error: Cannot have both \`command\` and \`url\`
**CORRECT - Minimal stdio:**
\`\`\`json
{
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-time"]
}
\`\`\`
**CORRECT - Minimal http:**
\`\`\`json
{
"url": "http://localhost:3000/sse"
}
\`\`\`
### Using addMcpServer Tool
**Example 1: Add stdio server**
\`\`\`json
{
"serverName": "filesystem",
"serverType": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/data"]
}
\`\`\`
**Example 2: Add HTTP server**
\`\`\`json
{
"serverName": "custom-api",
"serverType": "http",
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer token123"
}
}
\`\`\`
**Example 3: Add Python MCP server**
\`\`\`json
{
"serverName": "github",
"serverType": "stdio",
"command": "python",
"args": ["-m", "mcp_server_github"],
"env": {
"GITHUB_TOKEN": "ghp_xxxxx"
}
}
\`\`\`
## Operator actions
1. Use \`listMcpServers\` to enumerate configured servers.
2. Use \`listMcpTools\` for a server to understand the available operations and schemas.
3. Explain which MCP tools match the users needs before editing agent definitions.
4. When adding a tool to an agent, document what it does and ensure the schema mirrors the MCP definition.
2. Use \`addMcpServer\` to add or update MCP server configurations (with validation).
3. Use \`listMcpTools\` for a server to understand the available operations and schemas.
4. Explain which MCP tools match the user's needs before editing agent definitions.
5. When adding a tool to an agent, document what it does and ensure the schema mirrors the MCP definition.
## Example snippets to reference
- Firecrawl search (required param):
## Adding MCP Tools to Agents
Once an MCP server is configured, add its tools to agent definitions:
### MCP Tool Format in Agent
\`\`\`json
"tools": {
"descriptive_key": {
"type": "mcp",
"name": "actual_tool_name_from_server",
"description": "What the tool does",
"mcpServerName": "server_name_from_config",
"inputSchema": {
"type": "object",
"properties": {
"param1": {"type": "string", "description": "What param1 means"}
},
"required": ["param1"]
}
}
}
\`\`\`
### Tool Schema Rules
- Use \`listMcpTools\` to get the exact \`inputSchema\` from the server
- Copy the schema exactly as provided by the MCP server
- Only include \`"required"\` array if parameters are truly mandatory
- Add descriptions to help the agent understand parameter usage
### Example snippets to reference
- Firecrawl search (required param):
\`\`\`json
"tools": {
"search": {
"type": "mcp",
@ -34,8 +237,9 @@ Load this skill whenever a user asks about external tools, MCP servers, or how t
}
}
\`\`\`
- ElevenLabs text-to-speech (no required array):
\`\`\`
\`\`\`json
"tools": {
"text_to_speech": {
"type": "mcp",
@ -52,9 +256,13 @@ Load this skill whenever a user asks about external tools, MCP servers, or how t
}
\`\`\`
## Safety reminders
- Only recommend MCP tools that are actually configured.
- Clarify any missing details (required parameters, server names) before modifying files.
- ALWAYS use \`addMcpServer\` to configure MCP servers—never manually edit config files
- Only recommend MCP tools that are actually configured (use \`listMcpServers\` first)
- Clarify any missing details (required parameters, server names) before modifying files
- Test server connection with \`listMcpTools\` after adding a new server
- Invalid MCP configs prevent agents from startingvalidation is critical
`;
export default skill;

View file

@ -19,7 +19,45 @@ Load this skill whenever a user wants to inspect, create, or update agents insid
3. The orchestrator calls other agents as tools when needed
4. Data flows through tool call parameters and responses
## Agent format
## Agent File Schema
Agent files MUST conform to this exact schema. Invalid agents will fail to load.
### Complete Agent Schema
\`\`\`json
{
"name": "string (REQUIRED, must match filename without .json)",
"description": "string (REQUIRED, what this agent does)",
"instructions": "string (REQUIRED, detailed instructions for the agent)",
"model": "string (OPTIONAL, e.g., 'gpt-5.1', 'claude-sonnet-4-5')",
"provider": "string (OPTIONAL, provider alias from models.json)",
"tools": {
"descriptive_key": {
"type": "builtin | mcp | agent (REQUIRED)",
"name": "string (REQUIRED)",
// Additional fields depend on type - see below
}
}
}
\`\`\`
### Required Fields
- \`name\`: Agent identifier (must exactly match the filename without .json)
- \`description\`: Brief description of agent's purpose
- \`instructions\`: Detailed instructions for how the agent should behave
### Optional Fields
- \`model\`: Model to use (defaults to model config if not specified)
- \`provider\`: Provider alias from models.json (optional)
- \`tools\`: Object containing tool definitions (can be empty or omitted)
### Naming Rules
- Agent filename MUST match the \`name\` field exactly
- Example: If \`name\` is "summariser_agent", file must be "summariser_agent.json"
- Use lowercase with underscores for multi-word names
- No spaces or special characters in names
### Agent Format Example
\`\`\`json
{
"name": "agent_name",
@ -43,9 +81,26 @@ Load this skill whenever a user wants to inspect, create, or update agents insid
}
\`\`\`
## Tool types
## Tool Types & Schemas
### Builtin tools
Tools in agents must follow one of three types. Each has specific required fields.
### 1. Builtin Tools
Internal Rowboat tools (executeCommand, file operations, MCP queries, etc.)
**Schema:**
\`\`\`json
{
"type": "builtin",
"name": "tool_name"
}
\`\`\`
**Required fields:**
- \`type\`: Must be "builtin"
- \`name\`: Builtin tool name (e.g., "executeCommand", "readFile")
**Example:**
\`\`\`json
"bash": {
"type": "builtin",
@ -53,7 +108,42 @@ Load this skill whenever a user wants to inspect, create, or update agents insid
}
\`\`\`
### MCP tools
**Available builtin tools:**
- \`executeCommand\` - Execute shell commands
- \`readFile\`, \`createFile\`, \`updateFile\`, \`deleteFile\` - File operations
- \`listFiles\`, \`exploreDirectory\` - Directory operations
- \`analyzeAgent\` - Analyze agent structure
- \`addMcpServer\`, \`listMcpServers\`, \`listMcpTools\` - MCP management
- \`loadSkill\` - Load skill guidance
### 2. MCP Tools
Tools from external MCP servers (APIs, databases, web scraping, etc.)
**Schema:**
\`\`\`json
{
"type": "mcp",
"name": "tool_name_from_server",
"description": "What the tool does",
"mcpServerName": "server_name_from_config",
"inputSchema": {
"type": "object",
"properties": {
"param": {"type": "string", "description": "Parameter description"}
},
"required": ["param"]
}
}
\`\`\`
**Required fields:**
- \`type\`: Must be "mcp"
- \`name\`: Exact tool name from MCP server
- \`description\`: What the tool does (helps agent understand when to use it)
- \`mcpServerName\`: Server name from config/mcp.json
- \`inputSchema\`: Full JSON Schema object for tool parameters
**Example:**
\`\`\`json
"search": {
"type": "mcp",
@ -70,17 +160,40 @@ Load this skill whenever a user wants to inspect, create, or update agents insid
}
\`\`\`
### Agent tools (for chaining agents)
**Important:**
- Use \`listMcpTools\` to get the exact inputSchema from the server
- Copy the schema exactlydon't modify property types or structure
- Only include \`"required"\` array if parameters are mandatory
### 3. Agent Tools (for chaining agents)
Reference other agents as tools to build multi-agent workflows
**Schema:**
\`\`\`json
{
"type": "agent",
"name": "target_agent_name"
}
\`\`\`
**Required fields:**
- \`type\`: Must be "agent"
- \`name\`: Name of the target agent (must exist in agents/ directory)
**Example:**
\`\`\`json
"summariser": {
"type": "agent",
"name": "summariser_agent"
}
\`\`\`
**How it works:**
- Use \`"type": "agent"\` to call other agents as tools
- The target agent will be invoked with the parameters you pass
- Results are returned as tool output
- This is how you build multi-agent workflows
- The referenced agent file must exist (e.g., agents/summariser_agent.json)
## Complete Multi-Agent Workflow Example
@ -156,13 +269,88 @@ Load this skill whenever a user wants to inspect, create, or update agents insid
5. **Tool naming**: Use descriptive tool keys (e.g., "summariser", "fetch_data", "analyze")
6. **Orchestration**: Create a top-level agent that coordinates the workflow
## Validation & Best Practices
### CRITICAL: Schema Compliance
- Agent files MUST have \`name\`, \`description\`, and \`instructions\` fields
- Agent filename MUST exactly match the \`name\` field
- Tools MUST have valid \`type\` ("builtin", "mcp", or "agent")
- MCP tools MUST have all required fields: name, description, mcpServerName, inputSchema
- Agent tools MUST reference existing agent files
- Invalid agents will fail to load and prevent workflow execution
### File Creation/Update Process
1. When creating an agent, use \`createFile\` with complete, valid JSON
2. When updating an agent, read it first with \`readFile\`, modify, then use \`updateFile\`
3. Validate JSON syntax before writingmalformed JSON breaks the agent
4. Test agent loading after creation/update by using \`analyzeAgent\`
### Common Validation Errors to Avoid
**WRONG - Missing required fields:**
\`\`\`json
{
"name": "my_agent"
// Missing description and instructions
}
\`\`\`
**WRONG - Filename mismatch:**
- File: agents/my_agent.json
- Content: {"name": "myagent", ...}
**WRONG - Invalid tool type:**
\`\`\`json
"tool1": {
"type": "custom", // Invalid type
"name": "something"
}
\`\`\`
**WRONG - MCP tool missing required fields:**
\`\`\`json
"search": {
"type": "mcp",
"name": "firecrawl_search"
// Missing: description, mcpServerName, inputSchema
}
\`\`\`
**CORRECT - Minimal valid agent:**
\`\`\`json
{
"name": "simple_agent",
"description": "A simple agent",
"instructions": "Do simple tasks"
}
\`\`\`
**CORRECT - Complete MCP tool:**
\`\`\`json
"search": {
"type": "mcp",
"name": "firecrawl_search",
"description": "Search the web",
"mcpServerName": "firecrawl",
"inputSchema": {
"type": "object",
"properties": {
"query": {"type": "string"}
}
}
}
\`\`\`
## Capabilities checklist
1. Explore \`agents/\` directory to understand existing agents before editing
2. Update files carefully to maintain schema validity
3. When creating multi-agent workflows, create an orchestrator agent
4. Add other agents as tools with \`"type": "agent"\` for chaining
5. List and explore MCP servers/tools when users need new capabilities
6. Confirm work done and outline next steps once changes are complete
2. Read existing agents with \`readFile\` before making changes
3. Validate all required fields are present before creating/updating agents
4. Ensure filename matches the \`name\` field exactly
5. Use \`analyzeAgent\` to verify agent structure after creation/update
6. When creating multi-agent workflows, create an orchestrator agent
7. Add other agents as tools with \`"type": "agent"\` for chaining
8. Use \`listMcpServers\` and \`listMcpTools\` when adding MCP integrations
9. Confirm work done and outline next steps once changes are complete
`;
export default skill;

View file

@ -12,19 +12,6 @@ let modelConfig: z.infer<typeof ModelConfig> | null = null;
const baseMcpConfig: z.infer<typeof McpServerConfig> = {
mcpServers: {
firecrawl: {
command: "npx",
args: ["-y", "supergateway", "--stdio", "npx -y firecrawl-mcp"],
env: {
FIRECRAWL_API_KEY: "fc-aaacee4bdd164100a4d83af85bef6fdc",
},
},
test: {
url: "http://localhost:3000",
headers: {
"Authorization": "Bearer test",
},
},
}
};

View file

@ -0,0 +1,12 @@
import z from "zod"
import { Agent } from "./agent.js"
import { McpServerDefinition } from "./mcp.js"
export const Example = z.object({
id: z.string(),
instructions: z.string().optional(),
description: z.string().optional(),
entryAgent: z.string().optional(),
agents: z.array(Agent).optional(),
mcpServers: z.record(z.string(), McpServerDefinition).optional(),
});

View file

@ -1,16 +1,20 @@
import z from "zod";
import { z } from "zod";
const StdioMcpServerConfig = z.object({
export const StdioMcpServerConfig = z.object({
type: z.literal("stdio").optional(),
command: z.string(),
args: z.array(z.string()).optional(),
env: z.record(z.string(), z.string()).optional(),
});
const HttpMcpServerConfig = z.object({
export const HttpMcpServerConfig = z.object({
type: z.literal("http").optional(),
url: z.string(),
headers: z.record(z.string(), z.string()).optional(),
});
export const McpServerDefinition = z.union([StdioMcpServerConfig, HttpMcpServerConfig]);
export const McpServerConfig = z.object({
mcpServers: z.record(z.string(), z.union([StdioMcpServerConfig, HttpMcpServerConfig])),
});
mcpServers: z.record(z.string(), McpServerDefinition),
});

View file

@ -8,6 +8,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { Client } from "@modelcontextprotocol/sdk/client";
import { resolveSkill, availableSkills } from "../assistant/skills/index.js";
import { McpServerDefinition, McpServerConfig } from "../entities/mcp.js";
const BuiltinToolsSchema = z.record(z.string(), z.object({
description: z.string(),
@ -305,6 +306,118 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
},
},
addMcpServer: {
description: 'Add or update an MCP server in the configuration with validation. This ensures the server definition is valid before saving.',
inputSchema: z.object({
serverName: z.string().describe('Name/alias for the MCP server'),
serverType: z.enum(['stdio', 'http']).describe('Type of MCP server: "stdio" for command-based or "http" for HTTP/SSE-based'),
command: z.string().optional().describe('Command to execute (required for stdio type, e.g., "npx", "python", "node")'),
args: z.array(z.string()).optional().describe('Command arguments (optional, for stdio type)'),
env: z.record(z.string(), z.string()).optional().describe('Environment variables (optional, for stdio type)'),
url: z.string().optional().describe('HTTP/SSE endpoint URL (required for http type)'),
headers: z.record(z.string(), z.string()).optional().describe('HTTP headers (optional, for http type)'),
}),
execute: async ({ serverName, serverType, command, args, env, url, headers }: {
serverName: string;
serverType: 'stdio' | 'http';
command?: string;
args?: string[];
env?: Record<string, string>;
url?: string;
headers?: Record<string, string>;
}) => {
try {
// Build server definition based on type
let serverDef: any;
if (serverType === 'stdio') {
if (!command) {
return {
success: false,
message: 'For stdio type servers, "command" is required. Example: "npx" or "python"',
validationErrors: ['Missing required field: command'],
};
}
serverDef = {
type: 'stdio',
command,
...(args ? { args } : {}),
...(env ? { env } : {}),
};
} else if (serverType === 'http') {
if (!url) {
return {
success: false,
message: 'For http type servers, "url" is required. Example: "http://localhost:3000/sse"',
validationErrors: ['Missing required field: url'],
};
}
serverDef = {
type: 'http',
url,
...(headers ? { headers } : {}),
};
} else {
return {
success: false,
message: `Invalid serverType: ${serverType}. Must be "stdio" or "http"`,
validationErrors: [`Invalid serverType: ${serverType}`],
};
}
// Validate against Zod schema
const validationResult = McpServerDefinition.safeParse(serverDef);
if (!validationResult.success) {
return {
success: false,
message: 'Server definition failed validation. Check the errors below.',
validationErrors: validationResult.error.issues.map((e: any) => `${e.path.join('.')}: ${e.message}`),
providedDefinition: serverDef,
};
}
// Read existing config
const configPath = path.join(BASE_DIR, 'config', 'mcp.json');
let currentConfig: z.infer<typeof McpServerConfig> = { mcpServers: {} };
try {
const content = await fs.readFile(configPath, 'utf-8');
currentConfig = McpServerConfig.parse(JSON.parse(content));
} catch (error: any) {
if (error?.code !== 'ENOENT') {
return {
success: false,
message: `Failed to read existing MCP config: ${error.message}`,
};
}
// File doesn't exist, use empty config
}
// Check if server already exists
const isUpdate = !!currentConfig.mcpServers[serverName];
// Add/update server
currentConfig.mcpServers[serverName] = validationResult.data;
// Write back to file
await fs.mkdir(path.dirname(configPath), { recursive: true });
await fs.writeFile(configPath, JSON.stringify(currentConfig, null, 2), 'utf-8');
return {
success: true,
message: `MCP server '${serverName}' ${isUpdate ? 'updated' : 'added'} successfully`,
serverName,
serverType,
isUpdate,
configuration: validationResult.data,
};
} catch (error) {
return {
success: false,
message: `Failed to add MCP server: ${error instanceof Error ? error.message : 'Unknown error'}`,
};
}
},
},
listMcpServers: {
description: 'List all available MCP servers from the configuration',
inputSchema: z.object({}),