contentrain_submit
Push contentrain/* branches to remote for Contentrain MCP platform processing. This tool handles branch submission while PR creation is managed by the platform.
Instructions
Push contentrain/* branches to remote. MCP is push-only — PR creation is handled by the platform. Do NOT manually push or create PRs.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| branches | No | Specific branch names to push (omit for all contentrain/* branches) | |
| message | No | Optional message for the push operation |
Implementation Reference
- The implementation of the contentrain_submit tool, which pushes local contentrain/* git branches to a configured remote.
server.tool( 'contentrain_submit', 'Push contentrain/* branches to remote. MCP is push-only — PR creation is handled by the platform. Do NOT manually push or create PRs.', { branches: z.array(z.string()).optional().describe('Specific branch names to push (omit for all contentrain/* branches)'), message: z.string().optional().describe('Optional message for the push operation'), }, async (input) => { const config = await readConfig(projectRoot) if (!config) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'Project not initialized. Run contentrain_init first.' }) }], isError: true, } } const git = simpleGit(projectRoot) const remoteName = process.env['CONTENTRAIN_REMOTE'] ?? 'origin' // Check remote exists let hasRemote = false try { const remotes = await git.getRemotes() hasRemote = remotes.some(r => r.name === remoteName) } catch { hasRemote = false } if (!hasRemote) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: `No remote "${remoteName}" found. Configure a git remote first.`, next_steps: [`git remote add ${remoteName} <url>`], }) }], isError: true, } } try { // Determine the base branch for merge-status checks const baseBranch = config.repository?.default_branch ?? process.env['CONTENTRAIN_BRANCH'] ?? ((await git.raw(['branch', '--show-current'])).trim() || 'main') // Get contentrain branches const branchSummary = await git.branchLocal() // Get branches NOT yet merged into base (only these need pushing) let unmergedRaw = '' try { unmergedRaw = await git.raw(['branch', '--no-merged', baseBranch]) } catch { // If base branch doesn't exist yet, treat all as unmerged unmergedRaw = branchSummary.all.join('\n') } const unmergedSet = new Set( unmergedRaw.split('\n').map(b => b.replace(/^\*?\s+/, '').trim()).filter(Boolean), ) let branchesToPush: string[] if (input.branches && input.branches.length > 0) { branchesToPush = input.branches.filter(b => branchSummary.all.includes(b)) const missing = input.branches.filter(b => !branchSummary.all.includes(b)) if (missing.length > 0) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: `Branches not found: ${missing.join(', ')}`, }) }], isError: true, } } // Filter out already-merged branches from explicit list branchesToPush = branchesToPush.filter(b => unmergedSet.has(b)) } else { branchesToPush = branchSummary.all.filter(b => b.startsWith('contentrain/') && unmergedSet.has(b)) } if (branchesToPush.length === 0) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'No unmerged contentrain/* branches found to push.', next_steps: ['Make changes first with contentrain_content_save or contentrain_model_save'], }) }], isError: true, } } // Push each branch const pushed: string[] = [] const errors: Array<{ branch: string; error: string }> = [] for (const branch of branchesToPush) { try { await git.push(remoteName, branch) pushed.push(branch) } catch (error) { errors.push({ branch, error: error instanceof Error ? error.message : String(error), }) } } // Lazy cleanup: delete merged branches after push const cleanup = await cleanupMergedBranches(projectRoot) const nextSteps: string[] = [] if (pushed.length > 0) nextSteps.push('Create PRs on your git platform for review') if (errors.length > 0) nextSteps.push('Fix push errors and retry') if (cleanup.remaining >= 50) nextSteps.push(`Warning: ${cleanup.remaining} active contentrain branches. Consider reviewing old branches.`) return { content: [{ type: 'text' as const, text: JSON.stringify({ status: 'committed', message: `Pushed ${pushed.length} branch(es) to ${remoteName}.`, pushed, errors: errors.length > 0 ? errors : undefined, remote: remoteName, cleanup: cleanup.deleted > 0 ? { deleted: cleanup.deleted, remaining: cleanup.remaining } : undefined, next_steps: nextSteps, }, null, 2) }], } } catch (error) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: `Submit failed: ${error instanceof Error ? error.message : String(error)}`, }) }], isError: true, } } }, )