Skip to content

MCP tools

Reference

Greenlight exposes its functionality to coding agents as a fixed catalog of MCP tools. This page is the canonical reference: every tool, what it takes, what it returns, and a small example.

The conceptual model is in The agent protocol. This page assumes you know what MCP is and how an agent calls a tool.

Every tool is served by the greenlight MCP server and grouped by area. Jump to a group:

AreaTools
App lifecycleregisterApp · getRepoAccess · getApp · listApps · addCoOwner · removeCoOwner
MarketplacediscoverApps · requestAppAccess
Context (Knowledge)knowledgeList · knowledgeGet · knowledgeSearch · knowledgePropose
PolicygetPolicies
IntegrationslistGrantableIntegrations · requestCredentialAccess · getPermissions
Environment variablesenvList · envSet · envRemove
Local developmentapproveCliSession · inspectAppDb · inspectIntegrationApi · inspectAppBlob
Pipeline & operationscreatePullRequest · mergePullRequest · getPipelineRun · getLogs · getMetrics · getMetricsSeries · getAppDiagnostics · curlApp · getAppPreviewUrl
  • Tool names are flat camelCase (no dots) and served by the greenlight MCP server. Related tools share a name prefix (e.g. env*, knowledge*, inspect*).
  • Authorization is bound to the user the agent session is authenticated as. The agent cannot do anything the user couldn’t.
  • Environment — Each app has one environment. Tools that accept env default to shared when omitted.
  • Infrastructure is declarative. Workloads, resources (Postgres, blob), and integration grants are declared in greenlight.yml and applied by the merge-time reconciler — there is no MCP tool that provisions them. The catalog covers app lifecycle, env-var values, the pull-request flow, local development, and observability; the declared shape is read back through getApp.
  • Env writes take effect on deploy. envSet / envRemove store the value and return status: "complete"; the running app picks up the change on its next deploy.
  • No credential ever leaves Greenlight. No tool returns an upstream credential, a vault value, or a proxy/AI key. Tools that touch real data either run inside the control plane and return only results (inspect*), or deliver values to the laptop through the paired greenlight CLI — never through an MCP response.
  • Errors follow the error envelope. code is stable; messages are not.
  • Idempotency is supported on mutating tools that accept an idempotency_key. Repeated calls with the same key return the original response.
  • Pagination is cursor-based; see Errors & pagination.

Creates a new app as a scaffold — no cloud provisioning. It writes the apps row, the single shared environment row, and the SCM repository seeded with .gitignore, the Greenlight pipeline workflow, and a greenlight.yml carrying the app’s app_id, a docs.* block, and commented-out scaffold for workloads, resources, grants, and env. No Dockerfile is seeded — the agent authors it for the app’s stack in the first code PR. Workloads, resources, grants, and all infrastructure come from the first PR that declares them in greenlight.yml and merges. Registration is resumable and idempotent: calling it twice for the same slug resumes rather than creating a second app.

Input

{
"name": "Expense Tracker",
"slug": "expense-tracker",
"type": "server",
"description": "Quick reimbursement tracker for the finance team."
}

Output

{
"app_id": "app_k9x2m3p",
"slug": "expense-tracker",
"repo_full_name": "contoso/gl-app-expense-tracker",
"clone_url": "https://github.com/contoso/gl-app-expense-tracker",
"token_expires_at": "2026-03-12T15:08:11Z",
"registration_state": "complete",
"resumed": false
}

The way back into an existing project. Mints a fresh one-hour SCM token scoped to the app’s repo, so the agent can clone or pull. Called at the start of any session that needs to push code to an already-registered app, and again mid-session if the token is about to expire.

Input

{ "app_id": "app_k9x2m3p" }

Output

{
"clone_url": "https://github.com/contoso/gl-app-expense-tracker",
"token": "ghs_…",
"clone_command": "git clone https://x-access-token:ghs_…@github.com/contoso/gl-app-expense-tracker.git",
"authenticated_clone_url": "https://x-access-token:ghs_…@github.com/contoso/gl-app-expense-tracker.git",
"expires_at": "2026-03-12T16:08:11Z",
"repo_name": "gl-app-expense-tracker",
"repo_full_name": "contoso/gl-app-expense-tracker"
}

The token is a GitHub App installation token: it authenticates as the x-access-token user spliced into the HTTPS URL. clone_command and authenticated_clone_url are ready-to-run forms — the agent clones and pushes with those (or splices token into clone_url itself) rather than the gh CLI, which the agent’s session is usually not authenticated for.

Returns the current state of an app: the app row, its shared-environment summary, the declared manifest (resources, workloads, grants, env) projected from the last-merge SHA, per-grant approval status, the full env contract (user + managed names), and the latest pipeline run. Returns identifiers and metadata only — never raw manifest YAML, never env-var values.

Lists the caller’s apps, most recently deployed first (never-deployed apps last) — a citizen developer sees the apps they own or co-own; IT admin roles see every app in the organization. Cursor-paginated (limit 1–100, default 20).

Add or remove a co-owner on an app — used when a citizen developer says “let Bob help me with this.” Both take app_id, user_email, and an audit reason. The target must have logged into Greenlight at least once; otherwise addCoOwner returns user.not_found with the org login URL in next_steps.

The agent-facing counterpart to the dashboard Marketplace tab — browse every live, discoverable app in the org, not just the ones the caller already owns or co-owns. Cursor-paginated (limit 1–100, default 20), with optional slug (exact) and name_query (substring) filters. An app the caller can already open carries its deployment_url; one they can’t carries the same catalog-safe fields with deployment_url withheld and viewer_has_access: false — call requestAppAccess to ask for it.

Input

{ "name_query": "expense" }

Output

{
"items": [
{
"app_id": "app_k9x2m3p",
"slug": "expense-tracker",
"name": "Expense Tracker",
"owner_email": "avery@contoso.com",
"owner_name": "Avery Chen",
"deployment_url": null,
"viewer_has_access": false,
"access_request_status": null
}
],
"next_cursor": null
}

Requests the calling user’s own access to an app found via discoverApps or listApps — the imperative counterpart of requestCredentialAccess, but for a person’s access to an app instead of an integration credential. The signed-in user is always the one requesting; there’s no requesting on someone else’s behalf. Writes the same request the dashboard’s request-access form writes, with the same idempotency (an open request returns as-is) and re-request behavior (a denied or revoked request re-opens as pending). IT or the app’s owner approves, denies, or revokes from the dashboard — there is no agent decision tool.

Input

{ "slug": "expense-tracker", "reason": "Need to check my reimbursement status" }

Output

{
"grant_id": "5b8f0c2e-…",
"app_id": "app_k9x2m3p",
"slug": "expense-tracker",
"name": "Expense Tracker",
"status": "pending",
"requested_at": "2026-07-03T09:12:44.180Z",
"owner_email": "avery@contoso.com",
"owner_name": "Avery Chen"
}

Resources (Postgres, blob) and workloads (the app’s web server) are declared in greenlight.yml, not provisioned through MCP. The agent edits the resources: and workloads: blocks, opens a PR, and on merge the reconciler provisions the resources, renders the Kubernetes manifests, and rolls out the deploy. There is no resource.add tool; the declared and live state is read back through getApp, and merge-time provisioning outcomes surface through getPipelineRun.

Lists entry summaries for a scope.

Input

{
"scope": "org" | "integration" | "app",
"integration": "snowflake",
"app_id": "app_k9x2m3p",
"tags": ["data-modeling"],
"cursor": "..."
}

Output

{
"items": [
{ "id": "kn_pf2", "scope": "app", "topic": "architecture", "title": "App architecture", "version": 4, "updated_at": "2026-03-12T14:08:11Z" }
],
"next_cursor": null
}

Catalog and Custom integrations both start with no integration Knowledge; your team authors entries as it learns the source. If IT makes an integration inactive, all four Knowledge tools hide or reject its integration-scope entries for every agent role while the preserved content remains editable in the dashboard.

Returns one entry’s full body.

Input

{ "id": "kn_pf2" }

or

{ "scope": "app", "app_id": "app_k9x2m3p", "topic": "architecture" }

Output

{
"id": "kn_pf2",
"scope": "app",
"topic": "architecture",
"title": "App architecture",
"body_md": "## Overview\n\nThis app...",
"version": 4,
"updated_at": "2026-03-12T14:08:11Z",
"last_editor_kind": "agent"
}

Full-text search over titles and bodies.

Input

{ "query": "snowflake warehouse", "scope": "integration" }

Output

{
"items": [
{ "id": "kn_a1", "title": "Snowflake warehouses we use", "scope": "integration", "snippet": "...the `ANALYTICS_PROD` warehouse..." }
],
"next_cursor": null
}

The agent write path. Always writes to the proposal queue; never mutates entries directly.

Input

{
"scope": "app",
"app_id": "app_k9x2m3p",
"topic": "architecture",
"title": "App architecture",
"body_md": "...",
"rationale": "Discovered a clean way to split the cron job from the API.",
"base_version": 3
}

Output

{
"proposal_id": "kp_xyz",
"status": "pending"
}

Returns the active policy bundle for the org. Agents call this before generating code to know the rules.

Input

{ "kind": "approved-base-images" }

kind is optional; omit to get the full bundle.

Output

{
"policies": [
{ "id": "approved-base-images", "kind": "deny_if", "version": 12, "body": { "match": { /* … */ } } }
]
}

Integration grants are declared in greenlight.yml, not requested through MCP. On merge, Greenlight diffs the declared grants: entries against the app’s permissions and queues IT review for any that are not auto-approved.

Each grant names the integration and the credential slug IT registered — a stable handle, not a read/write/access enum:

grants:
- integration: snowflake-prod
credential: analytics-read
reason: Read the sales mart for the dashboard.

Each integration carries an auth_mode and a delivery_mode; see Data brokering for what they mean.

Lists the available integrations your org has registered and the credential slugs each exposes — call it before requesting access either way: as grants: targets for an app manifest, or as requestCredentialAccess targets for your own personal access. For each integration it returns the identity model (auth_mode), how the credential reaches the app (delivery_mode), and the catalog entry it was registered from (catalog_key, null for a Custom integration). For each credential it returns a paste-ready manifest_grant_example and a request_example (the personal-access call), your own current grant status on it (caller_grant_status), and a plain-language label for what to obtain (credential_kind) with a docs_link to the upstream’s own instructions. Read-only, cursor-paginated, scoped to your org. Integrations IT has made inactive are omitted and behave as not found when a stale slug is used in a grant request. Never returns vault keys, credential values, or upstream tokens.

Input

{ "integration": "snowflake-prod", "cursor": "...", "limit": 20 }

All fields are optional; omit integration to list every registered integration.

Output

{
"items": [
{
"integration": "snowflake-prod",
"name": "Snowflake (Prod)",
"catalog_key": "snowflake",
"delivery_mode": "injected",
"env_var_name": "SNOWFLAKE_API_KEY",
"base_url": "https://acme.snowflakecomputing.com",
"auth_mode": "service_account",
"credentials": [
{
"slug": "analytics-read",
"scope": "Read access to the analytics warehouse",
"credential_kind": "Snowflake programmatic access token",
"docs_link": "https://docs.snowflake.com/en/user-guide/key-pair-auth",
"configured": true,
"approval_mode": "manual",
"manifest_grant_example": {
"integration": "snowflake-prod",
"credential": "analytics-read",
"yaml": "grants:\n - integration: snowflake-prod\n credential: analytics-read\n"
},
"request_example": {
"integration": "snowflake-prod",
"credential_slug": "analytics-read",
"call": "requestCredentialAccess({ integration: 'snowflake-prod', credential_slug: 'analytics-read', reason: '<why the user needs this>' })"
},
"caller_grant_status": "none"
}
],
"usage_note": "Injected delivery: once IT approves the grant, read the credential from process.env.SNOWFLAKE_API_KEY in the deployed app and call the upstream directly."
}
],
"next_cursor": null
}
  • catalog_key names the catalog entry the integration was registered from, or is null for a Custom integration (registered by hand). For Custom integrations, credential_kind is empty and docs_link may be null.
  • env_var_name is populated only when delivery_mode is "injected" — it’s the name the credential is delivered under at deploy. Proxied integrations leave it null; the app reaches them through the proxy base URL instead.
  • base_url is the registered upstream host the proxy calls for this integration — use it to recognize when an existing integration already covers an API you were about to ask for. It’s null for a connected database, which has no HTTP base URL.
  • A credential marked configured: false is listed but not yet servable — a grant naming it cannot be approved until IT finishes registration.
  • approval_mode is "auto" (the grant activates on merge) or "manual" (IT reviews it in the queue before the runtime value is delivered).
  • caller_grant_status is your own personal grant on this credential — none, pending, granted, denied, or revoked — so an agent never re-requests blindly.
  • A greenlight.yml grant or Knowledge lookup naming an integration that doesn’t exist or is inactive gets a not_found error listing the valid available slugs — the same set this tool returns — so a mistyped or abbreviated name (e.g. fmp for financial-modeling-prep) is correctable from the error alone without disclosing inactive integrations.

Requests personal access to an integration credential for the signed-in user — for local work with no app, repo, or manifest. This is the imperative counterpart of an app’s declarative grants: block: an app’s access is declared in its manifest and activates at merge; a person’s access is requested with this call and reaches the same governed proxy under their own identity. The result is granted immediately when IT has set the credential to auto-approve, otherwise pending in IT’s review queue. Re-requesting after a denial or revocation re-opens the request as pending — it never overrides IT’s decision. Every request is recorded, including ones IT later denies.

Input

{ "integration": "snowflake-prod", "credential_slug": "analytics-read", "reason": "Explore the sales mart for a local dashboard" }

Output

{
"grant_id": "5b8f0c2e-…",
"integration": "snowflake-prod",
"credential_slug": "analytics-read",
"status": "granted",
"requested_at": "2026-07-03T09:12:44.180Z",
"policy_note": "auto-granted per credential approval mode"
}

Once granted, greenlight run (with no --app flag) delivers the access — see Local development. There is no dashboard form for this: requests come from the agent or CLI; the dashboard is where IT reviews them.

Lists integration grants for the app — declared grants from the manifest at the last-merge SHA, joined with approval status. Read-only. For each granted credential it also returns delivery_mode, which tells the agent how the integration runs locally: a granted integration is live (proxied through a minted token, or injected raw in-process); only user-delegated sources are fixture-only. An injected grant also carries its target env_var_name — including while status is still "pending" (a manual-approval grant), so the agent can see the name it will get without being told a value is already live before IT approves and the app redeploys.

Input

{ "app_id": "app_k9x2m3p" }

Output

{
"items": [
{
"integration": "snowflake-prod",
"credential": "analytics-read",
"declared_in_manifest": true,
"status": "approved",
"delivery_mode": "injected",
"env_var_name": "SNOWFLAKE_API_KEY"
}
]
}

Manage env-var values on the app’s shared environment. Values live in the customer’s Key Vault at apps/<app-id>/env/shared/<name>; Postgres stores metadata only. The names that reach the pod are governed by greenlight.yml (the env: block, plus managed names derived from resources: and grants:).

  • set / remove — store the vault value and the metadata row, then return { plan, executed_ops, status, note }. They do not restart the running app; it picks up the change on its next deploy (the note field restates this). sensitive: true makes the value write-once — it is never returned after creation. Reserved system names (e.g. DATABASE_URL) are rejected. set requires a reason for the audit log.
  • list — returns the full env contract: user-declared names (with vault presence) and derived managed names. Plain values are returned only when reveal: true; sensitive values are never returned.
await envSet({
app_id: 'app_k9x2m3p',
name: 'APPROVAL_SECRET',
value: '',
env: 'shared',
sensitive: true,
reason: 'Signing secret for the approvals webhook.',
});

See App environment variables for reserved names, the plain-vs-sensitive model, and the MISSING_ENV_VALUE deploy gate.

The agent develops the app on its own machine without ever holding an upstream credential. Two surfaces serve this, and neither puts a secret in an MCP response: the bundled greenlight CLI runs the app with real values delivered straight into the process, and the inspect* tools read deployed data inside the control plane and return only results. Whether a given integration is live locally or fixture-only is IT’s choice, set per credential. The full loop is in The agent protocol.

Signs the bundled greenlight CLI in as the caller, over the already-authenticated MCP session — no separate browser login. The CLI ships inside the plugin, so there’s nothing to download. The developer runs greenlight pair, which prints a short code; the agent passes it here; the server mints the CLI its own Greenlight credential — bound to the caller (owner/co-owner) under their normal RBAC, the same credential the CLI uses for every command — and hands it to the CLI out of band, never in this response. The code is single-use. Returns no secret to the agent.

Input

{ "code": "ABCD-1234" }

Output

{
"session_id": "cli_sess_7h2",
"app_scope": ["app_k9x2m3p"],
"expires_at": "2026-03-12T18:08:11Z"
}

Once paired, the agent runs code with greenlight run, in one of two explicit modes. App modegreenlight run --app <app_id> -- <your dev command> — resolves the app’s environment contract and grants (the same ones the deployed pod runs on) and injects the values into the running process; there is no credentials file on disk. User modegreenlight run -- <cmd> with no --app — runs on the signed-in user’s own personal grants instead (see requestCredentialAccess), for local work with no app at all. Either way, proxied integrations are reached through the same public proxy URL the deployed app uses, authenticated with a short-lived token the CLI mints from its session; the upstream secret stays in Greenlight exactly as it does in production. Granted injected credentials are delivered as their raw value in-process — every delivery is audited under the developer’s name. The app’s own Postgres runs as a local fixture database — use inspectAppDb to read deployed rows.

Runs one read-only SQL statement against the app’s own provisioned Postgres, executed inside the control plane against the vaulted credential. Returns columns and a capped set of rows — never the credential. Always available for the app’s own database, so the agent can learn the real schema and data before writing code. Audited with the calling user.

Input

{
"app_id": "app_k9x2m3p",
"statement": "select status, count(*) from expenses group by 1",
"params": []
}

Output

{
"columns": [
{ "name": "status", "type": "text" },
{ "name": "count", "type": "int8" }
],
"rows": [["approved", 142], ["pending", 17]],
"row_count": 2,
"truncated": false
}

Performs one HTTP request against a granted proxied integration, through the same grant check, credential swap, and audit path as the data proxy, with the developer’s session as the principal. Returns the response — never the upstream credential. Always available for a granted proxied integration. Injected integrations are not reachable here (they have no proxy backend); investigate those through Knowledge, a greenlight run against the granted raw value, or fixtures.

Input

{
"app_id": "app_k9x2m3p",
"integration": "snowflake-prod",
"method": "POST",
"path": "/api/v2/statements",
"body": { "statement": "select current_warehouse()" }
}

Output

{
"status": 200,
"headers": { "content-type": "application/json" },
"body": { "data": [["ANALYTICS_PROD"]] },
"truncated": false
}

Inspects the app’s own provisioned blob container, executed inside the control plane — never returns a SAS or credential. Three actions: list (prefix filter, capped count), read (one blob’s bytes, size-capped; text/JSON inline, otherwise base64), and head (metadata only). Always available for the app’s own storage. Audited with the calling user.

Input

{ "app_id": "app_k9x2m3p", "action": "list", "prefix": "exports/" }

Output

{
"items": [
{ "key": "exports/2026-03.csv", "size": 20480, "content_type": "text/csv", "last_modified": "2026-03-12T14:08:11Z" }
],
"truncated": false
}

Open and merge pull requests on the app’s repo. Direct pushes to main are blocked by branch protection; every change goes through a PR and the Greenlight Review and Policy Check (scanners + policy). The agent confirms that check passed via getPipelineRun, then calls mergePullRequest with the app id, PR number, and the exact head commit SHA it observed pass (expected_head_sha); merging applies the manifest and triggers the deploy. The call fails closed with a structured error — never falling through to the SCM provider’s merge API — if the PR has moved to a new head since, or if that head has not passed review.

The single pipeline read tool. It returns one run with its per-check verdicts and is the agent’s wait primitive after pushing a branch or opening a PR — it can long-poll server-side up to max_wait_seconds. Pass detail: "full" to enrich every check with structured remediation detail (failing file, line, rule, and a suggested fix) and, where available, a tail-capped log snapshot — so there’s no separate failure tool; the detail lives on each check. A failed deploy check also carries a blocker: whether the failure is a platform issue the agent should not try to fix in app code (owner: "operator", e.g. the cluster is out of schedulable capacity) or an app-side issue it can act on (owner: "agent"), plus a one-line explanation. Wait by commit_sha when the exact head must be confirmed — waiting by pull_request_number resolves to the newest recorded run, which can lag the current head after a force-push.

getLogs / getMetrics / getMetricsSeries / getAppDiagnostics / curlApp / getAppPreviewUrl

Section titled “getLogs / getMetrics / getMetricsSeries / getAppDiagnostics / curlApp / getAppPreviewUrl”

Runtime observability and smoke checks against the deployed shared environment. getLogs returns a bounded, paginated window of the pod’s merged stdout/stderr; getMetrics returns the most recent per-app CPU/memory/restart snapshot; getMetricsSeries returns a downsampled CPU or memory history over a window — bucketed averages plus a min/max/avg/p95 summary — so the agent can judge a trend without reading every sample; getAppDiagnostics returns one structured health snapshot (pod phase, restart reason, probe state, resource pressure, the env contract with values masked, dependency reachability, and freshness) — the consolidated “why is my deployed app misbehaving?” read; curlApp makes an authenticated server-side request into the deployed app with the same user and app-access decision as the SSO boundary, so the agent can confirm the app actually responds for a real authorized user after deploy. If the Service cannot complete a response, it returns app.unreachable; inspect details.hit_app, then call getAppDiagnostics and getLogs.

getAppPreviewUrl covers what curlApp can’t: seeing the app. It mints a one-time URL the agent opens in its own browser tooling (IDE preview pane, headless browser) to render pages, exercise client-side code, and capture screenshots. The embedded token is single-use, expires in five minutes, and is exchanged on first navigation for a short-lived session confined to that one app’s hostname — the session carries the agent user’s real identity, so access control and the audit trail behave exactly as if that person had signed in. SSO enforcement is never bypassed or weakened.

The two are complementary, and they even take different routes: curlApp goes server-side straight to the app inside the cluster, while a preview URL travels the full public path — DNS, TLS, and the SSO-enforcing ingress. When one succeeds and the other fails, that difference tells the agent whether the app or the edge is the problem. Agents default to curlApp for response checks (no credential ever leaves the platform) and mint a preview URL when verification needs a real browser. The CLI twin is greenlight curl --app <id> --path <path>; request headers and bodies are read from --headers-file, stdin, or --body-file, never placed directly in argv.

Files a concise Greenlight platform-experience report — a platform bug, recurring friction, or a concrete improvement idea — for the Greenlight platform team. It takes a category (bug / friction / suggestion / other), a one-line title, and a Markdown body_md (optionally an app_id and a small allowlisted context with the agent runtime, OS, plugin version, and related request_ids). The report is recorded with its source and calling surface derived server-side, attributed to the signed-in user, and returns a feedback id. Body content is treated as untrusted (capped, rendered inert on the dashboard) and submission is rate-limited per user, so reports should summarize the platform interaction — never secrets, env values, tokens, or verbatim sensitive content. The CLI twin is greenlight feedback (body via stdin or --body-file). Org admins review submissions on the dashboard Feedback tab, alongside reports filed by dashboard users through the shell “Send feedback” dialog.

Returns the full markdown of the universal Greenlight Builder Skill — the core greenlight Skill by default, or a focused Skill (connected-databases, platform-feedback) by skill name — byte-identical to the Skill file bundled in the plugin. Use it to re-fetch the Skill when the plugin skill isn’t loaded, after a context compaction, or when only MCP/CLI is reachable. An unrecognized skill name returns a skill.not_found error. This is a narrow, authenticated read of the same universal bytes every installation ships — not a per-org generation endpoint. The CLI twin is greenlight skill / greenlight skill show [--name <skill>].