# kcmd MCP server reference

> MCP server startup, workspace path binding, and agent tools for pull, push, list-entries, lookup-entry, and modify-entry in agentic metadata workflows.

- Repository: GoogleCloudPlatform/knowledge-catalog
- GitHub: https://github.com/GoogleCloudPlatform/knowledge-catalog
- Human docs: https://grok-wiki.com/public/docs/googlecloudplatform-knowledge-catalog-9cee6ee3cba5
- Complete Markdown: https://grok-wiki.com/public/docs/googlecloudplatform-knowledge-catalog-9cee6ee3cba5/llms-full.txt

## Source Files

- `agents/mdcode/src/tool/mcp.ts`
- `agents/mdcode/src/tool/main.ts`
- `agents/mdcode/README.md`
- `toolbox/mdcode/src/tool/mcp.ts`
- `toolbox/enrichment/README.md`
- `toolbox/enrichment/src/tools/md/server.ts`

---

---
title: "kcmd MCP server reference"
description: "MCP server startup, workspace path binding, and agent tools for pull, push, list-entries, lookup-entry, and modify-entry in agentic metadata workflows."
---

The `kcmd mcp` subcommand starts a stdio Model Context Protocol (MCP) server inside the same `kcmd` binary that powers the Metadata as Code CLI. At startup the server binds to one workspace directory, loads `catalog.yaml`, and exposes three tools—`list-entries`, `lookup-entry`, and `modify-entry`—that read and write local catalog artifacts through `CatalogSnapshot`. Catalog synchronization (`pull` and `push`) runs through the CLI, not as MCP tools; agent workflows typically combine MCP entry editing with CLI sync steps.

```mermaid
sequenceDiagram
  participant Agent as MCP client agent
  participant MCP as kcmd MCP server
  participant Snap as CatalogSnapshot
  participant Disk as catalog/ workspace
  participant CLI as kcmd CLI
  participant API as Knowledge Catalog API

  Agent->>MCP: stdio connect (kcmd mcp --path WORKSPACE)
  MCP->>Snap: CatalogSnapshot.fromPath(WORKSPACE)
  Snap->>Disk: load catalog.yaml + index entries

  Agent->>MCP: list-entries
  MCP->>Snap: listEntries()
  Snap-->>Agent: JSON array of entry names

  Agent->>MCP: lookup-entry(name)
  MCP->>Snap: lookupEntry(name)
  Snap->>Disk: merge .yaml + .ref.yaml + sidecars
  Snap-->>Agent: JSON Entry object

  Agent->>MCP: modify-entry(name, field, updates)
  MCP->>Snap: updateEntry(...)
  Snap->>Disk: write editable layer
  Snap-->>Agent: JSON updated Entry

  Note over Agent,CLI: Sync is CLI-only today
  Agent->>CLI: kcmd pull / kcmd push
  CLI->>API: CatalogSync.pull() / .push()
  CLI->>Disk: refresh local snapshot
```

## Prerequisites

<Steps>
<Step title="Initialize a workspace">

Create a kcmd workspace with `catalog.yaml` and a pulled snapshot before starting the MCP server. See [Sync catalog metadata](/sync-catalog-metadata) for `init`, `pull`, and `reference` steps.

</Step>
<Step title="Authenticate with gcloud ADC">

The MCP server calls `gcp.ApiContext.default()` at startup to load Dataplex entry and aspect type definitions. Configure Application Default Credentials:

```bash
gcloud auth application-default login
gcloud config set project YOUR_PROJECT_ID
```

</Step>
<Step title="Install or build kcmd">

Use the published npm package or a local build from `agents/mdcode`:

<CodeGroup>
```bash title="npm package"
npm install -g kcmd
```

```bash title="Local build"
cd agents/mdcode
npm install && npm run build
# binary: dist/kcmd
```
</CodeGroup>

</Step>
</Steps>

## Start the server

The `mcp` command registers with the `kcmd` CLI and delegates to `startServer()` in the MCP module.

| Item | Value |
|------|-------|
| Command | `kcmd mcp` |
| Transport | `StdioServerTransport` (stdio JSON-RPC) |
| Server name | `kcmd` |
| Server version | `1.0.0` |
| SDK | `@modelcontextprotocol/sdk` |

<ParamField body="--path" type="string">
Absolute or relative path to the workspace root—the directory that contains `catalog.yaml`. Always pass an explicit absolute path in MCP client configuration to avoid ambiguity when the client cwd differs from the workspace.
</ParamField>

<RequestExample>

```bash title="Start MCP server"
kcmd mcp --path /absolute/path/to/workspace
```

</RequestExample>

<Note>
The server loads the workspace snapshot once at startup. External changes made while the server is running—such as `kcmd pull` from another terminal—are not visible until you restart the MCP process.
</Note>

### MCP client configuration

Register the server in your agent's MCP settings. The pattern works with any MCP-capable client (Gemini CLI, Cursor, Claude Desktop, custom ADK runners).

<RequestExample>

```json title="mcp.json"
{
  "mcpServers": {
    "kcmd": {
      "command": "npx",
      "args": ["-y", "kcmd", "mcp", "--path", "/absolute/path/to/workspace"]
    }
  }
}
```

</RequestExample>

For local development, point `command` at the compiled binary:

```json
{
  "mcpServers": {
    "kcmd": {
      "command": "/path/to/agents/mdcode/dist/kcmd",
      "args": ["mcp", "--path", "/absolute/path/to/workspace"]
    }
  }
}
```

### Debug with MCP Inspector

From `agents/mdcode`, run the inspector against the compiled binary:

```bash
npm run x:mcp
# equivalent: npx @modelcontextprotocol/inspector dist/kcmd mcp
```

Pass `--path` through the inspector UI or append it to the spawned args.

## Workspace path binding

`startServer(basePath)` constructs a `CatalogSnapshot` from the workspace root:

1. **Manifest** — reads `catalog.yaml` at `{basePath}/catalog.yaml`; throws if missing.
2. **Layout selection** — `createLayout()` picks `StandardLayout` (YAML + sidecars) or `DocumentsLayout` (Markdown frontmatter) based on the manifest `scope`.
3. **Type registry** — fetches entry and aspect type definitions from Knowledge Catalog for types declared in `snapshot.entries` and `snapshot.aspects`.
4. **Entry index** — walks `catalog/` and indexes editable files (`.yaml` or `.md`) and reference layers (`.ref.yaml`).

Entry names returned by `list-entries` are the local `name` fields inside each artifact—for example `bigquery/my-project/my-dataset/my-table-id`—not Dataplex resource paths.

<Warning>
`modify-entry` writes only to the editable local layer. Reference files (`*.ref.yaml`) are read during `lookup-entry` merges but are never modified by MCP tools. Publishing reference-layer changes requires `kcmd push`, which skips `*.ref.yaml` files.
</Warning>

## MCP tools

The server registers three tools. All successful responses return MCP `content` with `type: "text"` and a pretty-printed JSON body. Errors set `isError: true` with a text message.

### `list-entries`

Lists every indexed entry name in the bound workspace.

| Field | Value |
|-------|-------|
| Parameters | None |
| Returns | JSON string array of entry names |
| Errors | Startup failures (missing manifest, auth, type load) surface before the tool is callable |

<ResponseExample>

```json title="list-entries response"
[
  "bigquery/my-project/my-dataset/events",
  "bigquery/my-project/my-dataset/products"
]
```

</ResponseExample>

### `lookup-entry`

Returns the full metadata for one entry, including merged reference and local layers plus Markdown sidecar content.

<ParamField body="name" type="string" required>
Local entry name as returned by `list-entries`.
</ParamField>

<ResponseField name="content" type="text">
JSON-serialized `Entry` object with `name`, `type`, `resource`, optional `aspects`, and optional `links`.
</ResponseField>

For Standard layout entries, `lookup-entry` merges `.ref.yaml` under the editable `.yaml` (local overrides reference) and inlines sidecar `.md` bodies into aspect `content` fields.

<RequestExample>

```json title="lookup-entry input"
{
  "name": "bigquery/my-project/my-dataset/events"
}
```

</RequestExample>

<ResponseExample>

```json title="lookup-entry response (excerpt)"
{
  "name": "bigquery/my-project/my-dataset/events",
  "type": "dataplex-types.global.bigquery-table",
  "resource": {
    "displayName": "events",
    "description": "GA4 ecommerce events table"
  },
  "aspects": {
    "dataplex-types.global.schema": {
      "fields": [{ "name": "event_name", "dataType": "STRING" }]
    },
    "dataplex-types.global.overview": {
      "content": "# Events\n\nSession and purchase events.",
      "contentType": "MARKDOWN"
    }
  }
}
```

</ResponseExample>

On failure (unknown name, missing files), the tool returns `isError: true` with message `Error looking up entry: …`.

### `modify-entry`

Updates one field on a local entry and persists the change to disk. Returns the post-update entry from a fresh `lookup-entry`.

<ParamField body="name" type="string" required>
Entry name to modify.
</ParamField>

<ParamField body="field" type="string" required>
Either `resource` or an aspect key (for example `dataplex-types.global.overview` or a manifest alias like `overview`).
</ParamField>

<ParamField body="updates" type="object" required>
Structured JSON dictionary with the new values for the targeted field.
</ParamField>

#### Field-specific behavior

| `field` value | What `updates` replaces | Persistence notes |
|---------------|-------------------------|-------------------|
| `resource` | Only `resource.description` is applied from `updates` | Other resource keys in `updates` are ignored by `updateEntry` |
| Aspect key | The entire aspect object for that key | Markdown aspects with `content` may be written to `.overview.md` sidecars |

<RequestExample>

```json title="modify-entry — update overview aspect"
{
  "name": "bigquery/my-project/my-dataset/events",
  "field": "dataplex-types.global.overview",
  "updates": {
    "content": "# Events\n\nEnriched by an agent.",
    "contentType": "MARKDOWN"
  }
}
```

</RequestExample>

<RequestExample>

```json title="modify-entry — update resource description"
{
  "name": "bigquery/my-project/my-dataset/events",
  "field": "resource",
  "updates": {
    "description": "GA4 obfuscated ecommerce events, partitioned by date."
  }
}
```

</RequestExample>

#### Modification constraints

- The aspect must appear in `snapshot.aspects` inside `catalog.yaml`; otherwise `updateEntry` throws `The aspect '…' is not registered in the snapshot.`
- For ingested entry groups (BigQuery, BigLake), required aspects on the entry type cannot be modified.
- Only aspects listed under `publishing.aspects` are pushed to Knowledge Catalog on `kcmd push`.
- `entryLinks` and `links` blocks are not directly exposed as MCP tools; manage links by editing YAML or using library APIs.

On failure, the tool returns `isError: true` with message `Error modifying entry: …`.

## Catalog sync: pull and push

`pull` and `push` are **CLI commands**, not MCP tools in the current implementation. Agentic metadata workflows use them alongside MCP entry editing:

```text
kcmd init …  →  kcmd pull  →  [agent: list / lookup / modify via MCP]  →  kcmd push
```

| Operation | CLI command | Role in agent workflows |
|-----------|-------------|-------------------------|
| Pull snapshot | `kcmd pull [--dry-run]` | Refresh local `catalog/` from Knowledge Catalog before agent work |
| Pull reference | `kcmd reference` | Add read-only `*.ref.yaml` grounding layers |
| Push changes | `kcmd push [--force] [--validate-only] [--dry-run]` | Publish MCP and manual edits back to the catalog |

<Info>
The design specification lists `pull` and `push` as planned MCP tools, but `agents/mdcode/src/tool/mcp.ts` registers only `list-entries`, `lookup-entry`, and `modify-entry`. Until sync tools ship over MCP, agents should shell out to `kcmd pull` and `kcmd push`, or embed `CatalogSnapshot` / `CatalogSync` from the `kcmd` library directly.
</Info>

See [kcmd CLI reference](/kcmd-cli-reference) for full flag documentation.

## Agent workflow patterns

### MCP-only editing loop

Suited for interactive agents (Gemini CLI, IDE MCP clients) that modify metadata in place:

1. `kcmd pull` to sync the latest remote state.
2. Start `kcmd mcp --path WORKSPACE` in the MCP client config.
3. Agent calls `list-entries` → `lookup-entry` per asset → `modify-entry` for aspects in `publishing.aspects`.
4. `kcmd push` to publish; use `--validate-only` first to catch schema errors.

### Library-embedded enrichment (no MCP)

The toolbox enrichment agent (`kcagent enrich`) imports `kcmd` as a library and registers a custom `update_documentation` `FunctionTool` that calls `catalog.updateEntry()` directly—same persistence path as `modify-entry`, without stdio MCP overhead. It loads additional grounding MCP servers (for example `md-fileset`) from `tools/mcp.json`. See [Toolbox enrichment demo](/toolbox-enrichment-demo).

### Composing multiple MCP servers

A typical enrichment workspace binds:

| Server | Purpose |
|--------|---------|
| `kcmd` | List, read, and write catalog entries in the mdcode workspace |
| `md-fileset` | Search and read organizational markdown for grounding |
| Custom servers | GitHub, Drive, or domain-specific context |

Provider-neutral pattern: each server is a stdio subprocess declared in `mcp.json`; the orchestrating agent merges tool sets regardless of model provider.

## Authentication and permissions

| Concern | Mechanism |
|---------|-----------|
| Token source | `gcloud auth application-default print-access-token` |
| Project / region | `gcloud config get-value project` and `compute/region` |
| Startup type loading | Read-only Dataplex API calls for entry/aspect type metadata |
| Push (CLI) | Requires catalog write permissions for the workspace `scope` |

Set `GCP_LOG=1` to enable verbose API logging from `ApiContext`.

## Troubleshooting

| Symptom | Likely cause | Verification |
|---------|--------------|--------------|
| Server exits immediately | Missing `catalog.yaml` at `--path` | Confirm `{path}/catalog.yaml` exists |
| `Unable to retrieve project, location, or token` | ADC not configured | Run `gcloud auth application-default login` |
| `The aspect '…' is not registered` | Aspect omitted from `snapshot.aspects` | Edit `catalog.yaml` and restart MCP |
| `The aspect '…' is not modifiable` | Required aspect on ingested entry | Target a non-required aspect (for example `overview`) |
| Agent sees stale entries after `kcmd pull` | Snapshot loaded at server start | Restart the MCP server process |
| Push rejects agent-written content | Aspect not in `publishing.aspects` or schema violation | Run `kcmd push --validate-only` |

See [Troubleshooting](/troubleshooting) for auth, billing, and push-conflict guidance.

## Related pages

<CardGroup>
<Card title="kcmd CLI reference" href="/kcmd-cli-reference">
`init`, `pull`, `push`, `reference`, and authentication flags that complement MCP entry tools.
</Card>
<Card title="Metadata as Code" href="/metadata-as-code">
Workspace model, layouts, reference layers, and entry link semantics the MCP server operates on.
</Card>
<Card title="Sync catalog metadata" href="/sync-catalog-metadata">
Initialize workspaces and run pull/push sync around agent editing sessions.
</Card>
<Card title="Publish enriched metadata" href="/publish-enriched-metadata">
Push MCP-edited workspaces and reconcile aspects without touching read-only reference layers.
</Card>
<Card title="Enrichment workflows" href="/enrichment-workflows">
End-to-end flows from source metadata through agent enrichment to catalog publication.
</Card>
<Card title="Toolbox enrichment demo" href="/toolbox-enrichment-demo">
TypeScript demo combining `kcmd` sync, `kcagent enrich`, and `md-fileset` MCP grounding.
</Card>
</CardGroup>
