@bettercms-ai/mcp 0.52.0 → 0.53.0

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/dist/index.js CHANGED
@@ -2433,6 +2433,16 @@ function structureResult(d) {
2433
2433
  const message = d?.message;
2434
2434
  return ok(typeof message === "string" ? message : "Updated the Content structure.", d);
2435
2435
  }
2436
+ var GIT_TEXT = {
2437
+ list_github_repos: "Every GitHub repository this workspace's connected GitHub App installations can reach, grouped by account. Use it to find the repository to import: each ACCOUNT carries the `installationId` that import_github_repo and fork_github_repo take, and each repository under it carries `owner`, `repo` and `defaultBranch`. An empty `accounts` list means nobody has connected GitHub to this workspace yet \u2014 tell the user to connect it from the dashboard (Project \u2192 Hosting \u2192 Connect GitHub) and call this again.",
2438
+ import_github_repo: "Connect an existing GitHub repository to the connected project \u2014 the agent's equivalent of the dashboard's Import from GitHub, and the thing to do BEFORE writing any code for a user who already has a repo. It provisions the deploy workflow, the project API key and the build variables inside the repository, records the connection, assigns the site handle, imports the repo's bcms-content.json as content models when it has one, and queues the first build. Pass `installationId`, `owner` and `repo` from list_github_repos; `branch` defaults to the repository's own default branch and becomes the branch every deploy builds from. It only works on a repository one of this workspace's installations can already reach \u2014 for anyone else's repository (a public starter, a template, another account's site) use fork_github_repo instead. Read `warning` / `contentWarning` / `actionsWarning` back to the user verbatim when they come back \u2014 they are the cases where the repository connected perfectly and still cannot build, or will leave the Pages tab empty. Re-importing replaces the project's existing connection.",
2439
+ fork_github_repo: "Fork ANY GitHub repository into the user's own account and import it in one step \u2014 how a user starts from a repository that is not theirs (a public starter, a template, another account's site). BetterCMS cannot wire a repository it has no installation on, so the copy has to live on their account first; this makes that copy, keeps the upstream link, and then connects it exactly as import_github_repo does. `installationId` names the DESTINATION account (from list_github_repos); `owner` and `repo` name the SOURCE, anywhere on GitHub. `name` renames the fork; `connect: false` forks without connecting. Forking is asynchronous: a 202 means the fork exists but GitHub is still copying it, and the answer tells you to call import_github_repo with the fork's owner and repo a moment later \u2014 never fork a second time.",
2440
+ list_github_branches: "The connected repository's branches, plus `buildBranch` \u2014 the one the provisioned Action builds from. A commit on any other branch never reaches the live site, so check this before you push.",
2441
+ list_github_commits: "The connected repository's commit history, newest first: sha, message, author, date and url. Defaults to the build branch; pass `branch` for another, `path` to narrow it to one file or directory, and `limit` (1-100, default 20). Read the head sha here before a push you want to be safe, and hand it back as push_to_github's `expectedHeadSha`.",
2442
+ push_to_github: "Commit files to the connected repository \u2014 a real git commit, written server-side with the GitHub App's own credentials, so you need no clone, no remote and no token of your own. Send `files` ([{path, content, encoding}]) and/or `deletePaths`, plus a `message`; both go into ONE commit, so a rename is atomic. It targets the BUILD branch by default, where the push starts the rebuild that goes live \u2014 poll get_deploy_status after it, and read the response's `note`, which says whether this push deploys. Pass `branch` to push somewhere else (a branch that does not exist yet is created from the build branch, or from `baseBranch`) and then open a pull request with create_github_pull_request. Pass `expectedHeadSha` from list_github_commits \u2014 a full sha or a prefix of one \u2014 to be refused with a 409 rather than silently overwrite a branch someone else moved while you worked. Limits, enforced BEFORE anything is written: 200 paths and 10 MB per commit, and `.git`, `.github/workflows`, `.env` and `.npmrc` are never writable \u2014 a refused patch writes nothing at all and names every offending path.",
2443
+ create_github_pull_request: "Open a pull request on the connected repository. `head` is the branch you pushed; `base` defaults to the build branch, so merging it is what ships the change. Use this instead of pushing straight to the build branch whenever a human should review first, or when that branch is protected. Returns the number and the url to hand the user.",
2444
+ list_github_pull_requests: "Pull requests on the connected repository: number, title, state, whether it merged, draft, head, base and url. `state` is 'open' (the default), 'closed' or 'all'. Poll it to find out whether the pull request you opened has landed."
2445
+ };
2436
2446
  function ok(summary, data) {
2437
2447
  return {
2438
2448
  content: [
@@ -3559,6 +3569,101 @@ ${res.warnings.join("\n")}` : summary, res.data);
3559
3569
  z.object({ uploadKey: z.string().min(1).describe("the uploadKey from create_deploy_upload, after PUTting the archive to its uploadUrl") }).shape,
3560
3570
  async (c, a) => ok("Deploy queued.", await data(c, "POST", `/management/projects/deploy/from-upload`, { uploadKey: a.uploadKey }))
3561
3571
  ),
3572
+ // ── Git (import a repository, read it, push commits back) ──
3573
+ // Same artifact:write authority as the deploy tools: on the build branch a push IS a
3574
+ // deploy. Every call runs server-side as the GitHub App installation, so the agent
3575
+ // needs no git remote, no clone and no token. @see management/git.ts
3576
+ def(
3577
+ "list_github_repos",
3578
+ "List importable GitHub repositories",
3579
+ GIT_TEXT.list_github_repos,
3580
+ z.object({}).shape,
3581
+ async (c) => ok("GitHub repositories.", await data(c, "GET", `/management/git/repos`))
3582
+ ),
3583
+ def(
3584
+ "import_github_repo",
3585
+ "Import a GitHub repository",
3586
+ GIT_TEXT.import_github_repo,
3587
+ z.object({
3588
+ installationId: z.number().int().positive().describe("from list_github_repos"),
3589
+ owner: z.string().min(1).describe("the repository's account, e.g. 'acme'"),
3590
+ repo: z.string().min(1).describe("the repository name, e.g. 'website'"),
3591
+ branch: z.string().min(1).optional().describe("branch to build from; defaults to the repository's own default branch")
3592
+ }).shape,
3593
+ async (c, a) => ok("Repository imported.", await data(c, "POST", `/management/git/connect`, { installationId: a.installationId, owner: a.owner, repo: a.repo, branch: a.branch }))
3594
+ ),
3595
+ def(
3596
+ "fork_github_repo",
3597
+ "Fork a repository and import it",
3598
+ GIT_TEXT.fork_github_repo,
3599
+ z.object({
3600
+ installationId: z.number().int().positive().describe("the DESTINATION account's installation, from list_github_repos"),
3601
+ owner: z.string().min(1).describe("the SOURCE repository's account, anywhere on GitHub"),
3602
+ repo: z.string().min(1).describe("the SOURCE repository name"),
3603
+ name: z.string().min(1).optional().describe("name for the fork; defaults to the source repository's name"),
3604
+ connect: z.boolean().optional().describe("connect the fork to this project (the default); false forks only")
3605
+ }).shape,
3606
+ async (c, a) => ok("Repository forked.", await data(c, "POST", `/management/git/fork`, { installationId: a.installationId, owner: a.owner, repo: a.repo, name: a.name, connect: a.connect }))
3607
+ ),
3608
+ def(
3609
+ "list_github_branches",
3610
+ "List the repository's branches",
3611
+ GIT_TEXT.list_github_branches,
3612
+ z.object({}).shape,
3613
+ async (c) => ok("Branches.", await data(c, "GET", `/management/git/branches`))
3614
+ ),
3615
+ def(
3616
+ "list_github_commits",
3617
+ "List the repository's commits",
3618
+ GIT_TEXT.list_github_commits,
3619
+ z.object({
3620
+ branch: z.string().min(1).optional().describe("defaults to the build branch"),
3621
+ path: z.string().min(1).optional().describe("only commits touching this file or directory"),
3622
+ limit: z.number().int().min(1).max(100).optional()
3623
+ }).shape,
3624
+ async (c, a) => ok("Commits.", await data(c, "GET", `/management/git/commits${q({ branch: a.branch, path: a.path, limit: a.limit })}`))
3625
+ ),
3626
+ def(
3627
+ "push_to_github",
3628
+ "Push a commit to the repository",
3629
+ GIT_TEXT.push_to_github,
3630
+ z.object({
3631
+ message: z.string().min(1).describe("the commit message"),
3632
+ files: z.array(z.object({
3633
+ path: z.string().min(1).describe("repository-relative path, e.g. 'src/pages/index.astro'"),
3634
+ content: z.string().describe("the file's contents"),
3635
+ encoding: z.enum(["utf8", "base64"]).optional().describe("'base64' for binary files; defaults to utf8")
3636
+ })).optional().describe("files to write or overwrite"),
3637
+ deletePaths: z.array(z.string().min(1)).optional().describe("paths to remove in the same commit"),
3638
+ branch: z.string().min(1).optional().describe("defaults to the build branch; a branch that does not exist is created"),
3639
+ baseBranch: z.string().min(1).optional().describe("what a newly created `branch` starts from; defaults to the build branch"),
3640
+ expectedHeadSha: z.string().min(7).optional().describe("the head you built this change on; the push is refused if the branch moved since")
3641
+ }).shape,
3642
+ async (c, a) => ok("Pushed.", await data(c, "POST", `/management/git/commit`, { message: a.message, files: a.files, deletePaths: a.deletePaths, branch: a.branch, baseBranch: a.baseBranch, expectedHeadSha: a.expectedHeadSha }))
3643
+ ),
3644
+ def(
3645
+ "create_github_pull_request",
3646
+ "Open a pull request",
3647
+ GIT_TEXT.create_github_pull_request,
3648
+ z.object({
3649
+ head: z.string().min(1).describe("the branch you pushed"),
3650
+ base: z.string().min(1).optional().describe("defaults to the build branch"),
3651
+ title: z.string().min(1),
3652
+ body: z.string().optional(),
3653
+ draft: z.boolean().optional()
3654
+ }).shape,
3655
+ async (c, a) => ok("Pull request opened.", await data(c, "POST", `/management/git/pulls`, { head: a.head, base: a.base, title: a.title, body: a.body, draft: a.draft }))
3656
+ ),
3657
+ def(
3658
+ "list_github_pull_requests",
3659
+ "List pull requests",
3660
+ GIT_TEXT.list_github_pull_requests,
3661
+ z.object({
3662
+ state: z.enum(["open", "closed", "all"]).optional(),
3663
+ limit: z.number().int().min(1).max(100).optional()
3664
+ }).shape,
3665
+ async (c, a) => ok("Pull requests.", await data(c, "GET", `/management/git/pulls${q({ state: a.state, limit: a.limit })}`))
3666
+ ),
3562
3667
  // ── Insight: what happened, what's working, what's broken ─────────────────
3563
3668
  def(
3564
3669
  "list_activity",