Init RESTForge Config
setup_init_configInitializes project configuration and sample payloads by creating config template, payload samples, and SQL query files.
Instructions
Generate skeleton config and sample payloads in the project folder via restforge-cli.
USE WHEN:
The project has restforgejs installed in node_modules
The config/ and payload/ folders do not exist yet, or you want to reset them to the default template
Starting RESTForge project configuration from scratch
DO NOT USE FOR:
Installing restforgejs -> use 'setup_install_package'
Filling in credentials in db-connection.env -> use 'setup_write_env'
This tool runs: npx restforge-cli init in the given cwd. Output: config/db-connection.env (empty template), payload/samples.json, payload/query/samples-datatables.sql.
PRESENTATION GUIDANCE:
Match the user's language. If the user writes in Indonesian, respond in Indonesian.
Never mention internal tool names in the reply to the user. Describe actions by what they do (e.g. "install the package", "fill in the credentials").
Speak in plain language. Summarise the result; do not paste raw CLI output unless the user explicitly asks.
When a precondition is not met, frame it as a question or next-step suggestion rather than an error.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| cwd | Yes | Absolute path of the project folder |
Implementation Reference
- src/tools/setup/init-config.ts:1-132 (handler)The main handler function `registerSetupInitConfig` that implements the 'setup_init_config' tool logic. It registers the tool with MCP server, defines the schema (cwd input), checks for restforgejs precondition, and runs `npx restforge-cli init` to generate skeleton config files.
import { z } from 'zod'; import { access } from 'node:fs/promises'; import { resolve, join } from 'node:path'; import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { execProcess } from '../../lib/exec.js'; export function registerSetupInitConfig(server: McpServer): void { server.registerTool( 'setup_init_config', { title: 'Init RESTForge Config', description: `Generate skeleton config and sample payloads in the project folder via restforge-cli. USE WHEN: - The project has restforgejs installed in node_modules - The config/ and payload/ folders do not exist yet, or you want to reset them to the default template - Starting RESTForge project configuration from scratch DO NOT USE FOR: - Installing restforgejs -> use 'setup_install_package' - Filling in credentials in db-connection.env -> use 'setup_write_env' This tool runs: npx restforge-cli init in the given cwd. Output: config/db-connection.env (empty template), payload/samples.json, payload/query/samples-datatables.sql. PRESENTATION GUIDANCE: - Match the user's language. If the user writes in Indonesian, respond in Indonesian. - Never mention internal tool names in the reply to the user. Describe actions by what they do (e.g. "install the package", "fill in the credentials"). - Speak in plain language. Summarise the result; do not paste raw CLI output unless the user explicitly asks. - When a precondition is not met, frame it as a question or next-step suggestion rather than an error.`, inputSchema: { cwd: z .string() .min(1) .describe('Absolute path of the project folder'), }, annotations: { title: 'Init Config', readOnlyHint: false, idempotentHint: false, }, }, async ({ cwd }) => { const projectCwd = resolve(cwd); // Precondition check: restforgejs must be present in node_modules. // Treated as a non-error precondition per the authoring guide §3.4. try { await access(join(projectCwd, 'node_modules', 'restforgejs')); } catch { return { content: [ { type: 'text', text: `Precondition not met: the RESTForge package is not installed in this project. Project path: ${projectCwd} Expected location: node_modules/restforgejs For the assistant: - The user needs to install the RESTForge package before the initial config can be generated. - Use the appropriate package-installation tool to do this, then retry generating the config. - When explaining to the user, say something like "the RESTForge package isn't installed yet — should I install it first?". Do not mention internal tool names.`, }, ], isError: false, }; } const result = await execProcess( 'npx', ['restforge-cli', 'init'], { cwd: projectCwd, timeout: 30_000 } ); // CLI failure: real error per §3.4; structured per §3.5. if (!result.success) { return { content: [ { type: 'text', text: `Failed to generate the initial RESTForge configuration. Project path: ${projectCwd} Command: ${result.command} Exit code: ${result.exitCode} --- CLI output --- stdout: ${result.stdout} stderr: ${result.stderr} --- end CLI output --- For the assistant: - Tell the user that the initialisation command did not complete successfully. - Summarise the likely cause from the CLI output in plain language; do not paste the raw stdout/stderr unless the user explicitly asks for it. - Offer to retry once the underlying issue is resolved. Do not mention internal tool names.`, }, ], isError: true, }; } // Success: one-line summary + labeled facts + fenced raw output per §3.5. return { content: [ { type: 'text', text: `Initial RESTForge configuration generated successfully. Project path: ${projectCwd} Files created: - config/db-connection.env (empty template, awaiting credentials) - payload/samples.json - payload/query/samples-datatables.sql --- CLI output --- ${result.stdout} --- end CLI output --- For the assistant: - Confirm to the user that the project skeleton is ready. - Suggest the next step in plain words: the credentials file (license and database connection) still needs to be filled in before the project can run. - Keep the reply concise. Do not paste the raw CLI output unless the user explicitly asks. Do not mention internal tool names.`, }, ], }; } ); } - src/tools/setup/init-config.ts:31-36 (schema)Input schema for the tool: requires a 'cwd' (absolute path of the project folder) string parameter with min length 1.
inputSchema: { cwd: z .string() .min(1) .describe('Absolute path of the project folder'), }, - src/tools/setup/index.ts:4-4 (registration)Import of `registerSetupInitConfig` from init-config module, used to register the tool.
import { registerSetupInitConfig } from './init-config.js'; - src/tools/setup/index.ts:15-15 (registration)Registration call: `registerSetupInitConfig(server)` inside `registerSetupTools`, which registers the tool with the MCP server.
registerSetupInitConfig(server); - src/lib/exec.ts:1-69 (helper)Utility helper `execProcess` used to run the `npx restforge-cli init` command. It wraps execa and returns a structured ExecResult with success, stdout, stderr, exitCode, and command.
import { execa, type ExecaError } from 'execa'; export interface ExecResult { success: boolean; stdout: string; stderr: string; exitCode: number; command: string; } export interface ExecOptions { cwd?: string; timeout?: number; /** * Environment variables for the subprocess. Merged with parent process env * via spread: { ...process.env, ...options.env }. Use this to suppress * tooling banners (e.g. NODE_ENV: 'production') or pass tool-specific config. */ env?: NodeJS.ProcessEnv; /** * Whether to strip the trailing newline from stdout/stderr. Default true * (matches execa default). Set to false when byte-perfect passthrough is * required (e.g. CLI commands that output template files where the final * newline matters). */ stripFinalNewline?: boolean; } /** * Execute subprocess dengan structured result. Tidak throw — selalu return result. * Cocok untuk wrapping CLI calls seperti npm, npx, restforge, restforge-cli. */ export async function execProcess( command: string, args: string[], options: ExecOptions = {} ): Promise<ExecResult> { const { cwd = process.cwd(), timeout = 60_000, env, stripFinalNewline = true } = options; const fullCommand = `${command} ${args.join(' ')}`; // Merge env: parent env first, custom env overrides const mergedEnv = env ? { ...process.env, ...env } : undefined; try { const result = await execa(command, args, { cwd, timeout, reject: false, stripFinalNewline, ...(mergedEnv ? { env: mergedEnv } : {}), }); return { success: result.exitCode === 0, stdout: result.stdout, stderr: result.stderr, exitCode: result.exitCode ?? -1, command: fullCommand, }; } catch (error) { const e = error as ExecaError; return { success: false, stdout: e.stdout?.toString() ?? '', stderr: e.stderr?.toString() ?? e.message, exitCode: e.exitCode ?? -1, command: fullCommand, }; } }