Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- `clgraph.mcp.config` - the MCP server now resolves what to index from the
project itself, so plugin hosts can launch it with no arguments. Order:
`--pipeline` argument, `$CLGRAPH_PIPELINE`, `clgraph.toml`,
`[tool.clgraph]` in `pyproject.toml`, then a cached
`.clgraph/pipeline.json`. Public API: `resolve()`, `load_pipeline()`,
`PipelineSource`.
- `clgraph-mcp` console script, so MCP client configs can invoke the server
directly instead of going through `python -m clgraph.mcp`.
- `clgraph.toml` / `[tool.clgraph]` project configuration, with keys
`sql_dir` and `dialect`.

### Changed

- **Breaking:** the SQL dialect is no longer defaulted to `bigquery` when
indexing SQL files. `clgraph-mcp --pipeline ./queries/` without
`--dialect` now exits with a message naming the fix, where it previously
parsed as BigQuery. sqlglot parses most of a corpus under the wrong
grammar without erroring, so the old default produced a lineage graph
that looked right and was not. JSON pipelines are unaffected - they
carry their own dialect.
- `create_mcp_server()` and `run_mcp_server()` accept `pipeline=None`. An
unconfigured server still registers every tool with its full schema, and
each returns a message pointing at setup. Previously a missing
`--pipeline` killed the process during the MCP handshake, which surfaces
in clients as a failed server with no explanation.
- `--pipeline` is now optional for `clgraph mcp` and `clgraph-mcp`.

## [0.0.8] - 2026-08-11

### Added
Expand Down
51 changes: 41 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,35 @@ Expose your pipeline's lineage tools to AI assistants via the [Model Context Pro
pip install 'clgraph[mcp]'
```

#### Project Configuration

The server works out what to index from the project itself, so hosts can
launch it with no arguments. Put this in `clgraph.toml` at your project root:

<!-- skip-test -->
```toml
sql_dir = "queries/"
dialect = "snowflake"
```

Or use `[tool.clgraph]` in `pyproject.toml` with the same keys. Resolution
order, first hit wins:

1. `--pipeline` / `--dialect` arguments
2. `$CLGRAPH_PIPELINE` / `$CLGRAPH_DIALECT`
3. `clgraph.toml`
4. `[tool.clgraph]` in `pyproject.toml`
5. A cached `.clgraph/pipeline.json`

An unconfigured project still starts a server. Every tool keeps its full
schema and reports that setup has not run yet, so an assistant can tell you
what to do instead of showing a dead connection.

**The dialect is never guessed.** Pointing clgraph at SQL files without
saying which dialect they are is an error, not a silent fall back to
BigQuery — sqlglot parses most of a corpus under the wrong grammar without
complaining, and the resulting lineage graph is plausible and wrong.

#### Claude Desktop Configuration

Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
Expand All @@ -782,8 +811,8 @@ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_
{
"mcpServers": {
"clgraph": {
"command": "python",
"args": ["-m", "clgraph.mcp", "--pipeline", "/path/to/your/sql/queries/"]
"command": "clgraph-mcp",
"env": { "CLGRAPH_PROJECT_DIR": "/path/to/your/project" }
}
}
}
Expand All @@ -794,19 +823,21 @@ Then ask Claude: *"What tables are in my pipeline?"*, *"Where does revenue.total
#### CLI Usage

```bash
# stdio transport (default, for Claude Desktop)
python -m clgraph.mcp --pipeline ./queries/
# stdio transport (default) — reads clgraph.toml
clgraph-mcp

# HTTP transport (for remote MCP clients)
python -m clgraph.mcp --pipeline ./queries/ --transport http
# Point at a directory explicitly (--dialect is required for SQL files)
clgraph-mcp --pipeline ./queries/ --dialect snowflake

# From a JSON pipeline file
python -m clgraph.mcp --pipeline pipeline.json
# HTTP transport (for remote MCP clients)
clgraph-mcp --pipeline ./queries/ --dialect snowflake --transport http

# Specify dialect (default: bigquery)
python -m clgraph.mcp --pipeline ./queries/ --dialect snowflake
# From a JSON pipeline file — it carries its own dialect
clgraph-mcp --pipeline pipeline.json
```

`python -m clgraph.mcp` and `clgraph mcp` accept the same options.

#### Programmatic Usage

<!-- skip-test -->
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ classifiers = [

dependencies = [
"sqlglot>=28.0.0,<31.0.0",
"tomli>=2.0.0; python_version < '3.11'",
"graphviz>=0.20.0",
"jinja2>=3.0.0",
"typer>=0.12.0",
Expand Down Expand Up @@ -102,6 +103,7 @@ Issues = "https://github.com/mingjerli/clgraph/issues"

[project.scripts]
clgraph = "clgraph.cli:app"
clgraph-mcp = "clgraph.mcp.server:main"

[tool.hatch.build.targets.wheel]
packages = ["src/clgraph"]
Expand Down
40 changes: 32 additions & 8 deletions src/clgraph/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import json
from enum import Enum
from pathlib import Path
from typing import Optional

import typer
from typing_extensions import Annotated
Expand Down Expand Up @@ -257,13 +258,13 @@ def _print_diff_summary(diff_result):
@app.command()
def mcp(
pipeline: Annotated[
Path,
Optional[Path],
typer.Option("--pipeline", "-p", help="Path to SQL files directory or JSON pipeline file"),
],
] = None,
dialect: Annotated[
str,
typer.Option(help="SQL dialect"),
] = "bigquery",
Optional[str],
typer.Option(help="SQL dialect. Required for SQL directories; never inferred."),
] = None,
transport: Annotated[
str,
typer.Option(help="Transport type: stdio, http"),
Expand All @@ -273,15 +274,27 @@ def mcp(
typer.Option("--no-llm-tools", help="Exclude LLM-dependent tools"),
] = False,
):
"""Start MCP server for LLM integration (Claude Desktop, etc.).
"""Start MCP server for LLM integration (Claude Code, Claude Desktop, etc.).

--pipeline is optional. Without it, the project's own configuration
decides what to index: $CLGRAPH_PIPELINE, then clgraph.toml, then
[tool.clgraph] in pyproject.toml, then a cached .clgraph/pipeline.json.
An unconfigured project still starts a server; its tools report that
setup has not run yet.

Requires the mcp extra: pip install clgraph[mcp]
"""
if not pipeline.exists():
from clgraph.mcp.config import load_pipeline, resolve

if pipeline is not None and not pipeline.exists():
typer.echo(f"Error: path does not exist: {pipeline}", err=True)
raise typer.Exit(code=1)

loaded = _load_pipeline(pipeline, dialect)
source = resolve(
Path.cwd(),
cli_path=str(pipeline) if pipeline is not None else None,
cli_dialect=dialect,
)

try:
from clgraph.mcp import run_mcp_server
Expand All @@ -292,6 +305,17 @@ def mcp(
)
raise typer.Exit(code=1) from err

if source is None:
typer.echo(
"No clgraph configuration found; starting unconfigured. "
"Run /clgraph:setup or pass --pipeline.",
err=True,
)
loaded = None
else:
typer.echo(f"Loading pipeline from {source.describe()}", err=True)
loaded = load_pipeline(source)

run_mcp_server(
loaded,
llm=None,
Expand Down
4 changes: 4 additions & 0 deletions src/clgraph/mcp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@
}
"""

from .config import PipelineSource, load_pipeline, resolve
from .server import create_mcp_server, run_mcp_server

__all__ = [
"create_mcp_server",
"run_mcp_server",
"PipelineSource",
"resolve",
"load_pipeline",
]
Loading
Loading