Skip to content

v0.6.0: Agent-first protocol — MCP server, REST API, 6 tools#4

Merged
vigneshnarayanaswamy merged 18 commits intomainfrom
vigneshn/v0.6.0-agent-protocol
Apr 10, 2026
Merged

v0.6.0: Agent-first protocol — MCP server, REST API, 6 tools#4
vigneshnarayanaswamy merged 18 commits intomainfrom
vigneshn/v0.6.0-agent-protocol

Conversation

@vigneshnarayanaswamy
Copy link
Copy Markdown
Collaborator

Summary

  • 6 consolidated agent tools (discover, record, investigate, query, trace, changelog) with Pydantic I/O schemas
  • MCP server (FastMCP) — model-ledger mcp starts a stdio server with 6 tools + 3 resources
  • REST API (FastAPI) — model-ledger serve starts HTTP server with auto-generated OpenAPI docs
  • JSON file backend — human-readable, git-friendly default storage
  • Demo mode--demo flag loads 7 sample models with events and dependencies
  • CLI commandsmcp and serve with --backend, --path, --demo flags
  • 198 new tests (573 total), all passing

Claude Code integration

pip install model-ledger[mcp]
claude mcp add model-ledger -- model-ledger mcp --demo

REST API for frontends

pip install model-ledger[rest-api]
model-ledger serve --demo
# OpenAPI docs at http://localhost:8000/docs

Test plan

  • 573 tests passing (198 new)
  • MCP server tested end-to-end with Claude Code — all 6 tools working
  • Ruff clean
  • Gitleaks CI green

🤖 Generated with Claude Code

Vignesh Narayanaswamy and others added 18 commits April 9, 2026 19:07
Create the tools package with all 16 Pydantic v2 models covering 6 tool
contracts (record, query, investigate, trace, changelog, discover) plus
4 shared types (ModelSummary, EventSummary, EventDetail, DependencyNode).
These schemas are the single source of truth for MCP, REST API, and CLI.
72 tests covering all fields, defaults, JSON round-trip, and JSON Schema export.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the query tool with structured filters (model_type, owner,
status), case-insensitive text search on name/purpose, and pagination.
Includes _model_to_summary helper for reuse by the discover tool.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the discover tool for bulk model ingestion from inline data,
JSON files, or connectors. Inline and file sources convert dicts to
DataNodes and leverage ledger.add() for content-hash dedup and
ledger.connect() for auto-linking dependencies. Connector source raises
NotImplementedError pending SDK-direct usage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the changelog tool with time range filtering (since/until),
model_name and event_type filters, pagination, and newest-first ordering.
Includes 15 tests covering all specified behaviors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Re-exports changelog, discover, investigate, query, record, and trace
from model_ledger.tools so consumers can import tool functions directly
from the package.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
JsonFileLedgerBackend stores models, snapshots, and tags as indented
JSON files in a directory tree. Implements full LedgerBackend protocol
including sorted snapshot listing and tag-based resolution. 23 tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a FastAPI app at model_ledger.rest.app with endpoints for all 6
agent protocol tools (record, discover, investigate, query, trace,
changelog) plus /overview. ModelNotFoundError maps to HTTP 404.
Adds rest-api optional dependency and E402 suppression for tests
using pytest.importorskip.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FastMCP server wrapping all tool functions (discover, record, investigate,
query, trace, changelog) with primitive-typed parameters for MCP compat.
Three resources: ledger://overview, ledger://schema, ledger://backends.
Includes create_server() factory and main() CLI entry point.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Repositions model-ledger as a fundamental model inventory (not
MRM-specific). Adds agent-first messaging, MCP/REST docs, tool
protocol overview, JSON file backend, and updated install profiles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Clear section hierarchy with progressive disclosure
- Fix architecture diagram (add SDK layer between tools and backends)
- Fix self-contained investigate example
- Trim agent conversation (3 → 2 exchanges)
- Collapse compliance/introspection into brief "Additional Capabilities"
- Add positioning line vs MLflow/SageMaker/W&B
- Remove "thin tools, thick descriptions" from public README (internal)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These docs reflect the old compliance-first positioning (v0.2-v0.3)
and are superseded by the v0.6.0 README. Archived to second brain.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Ledger.add() now reads model_type/type from DataNode metadata
  (was only reading node_type, which nobody set)
- investigate: skip internal events (depends_on, has_dependent,
  registered) and internal keys (_content_hash, upstream/downstream
  hashes) when merging metadata
- query: extract platform from discovered payload, not just
  snapshot.source
- demo: add model_type to all DataNode metadata

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vigneshnarayanaswamy vigneshnarayanaswamy merged commit c79239f into main Apr 10, 2026
3 checks passed
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a3fb8c0e75

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


def _resolve_backend(backend: str, path: str | None):
"""Resolve a backend name to a backend instance."""
if backend == "sqlite" and path:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use sqlite backend even when --path is omitted

When backend is set to sqlite, _resolve_backend only returns a SQLiteLedgerBackend if path is truthy; otherwise it returns None, and both mcp_cmd and serve_cmd fall back to the default in-memory backend. Running model-ledger mcp --backend sqlite (or serve) without --path therefore silently loses persistence, which is a high-impact mismatch with the user-selected backend.

Useful? React with 👍 / 👎.

Comment on lines +52 to +56
if input.model_type is not None:
filters["model_type"] = input.model_type
if input.owner is not None:
filters["owner"] = input.owner
if input.status is not None:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce platform filter in query tool

The query API exposes platform in QueryInput and the REST/MCP surface, but query() never applies input.platform in either backend filters or post-filtering. As a result, requests like platform="airflow" return models from other platforms, producing incorrect inventory slices for callers that rely on platform-scoped search.

Useful? React with 👍 / 👎.

# ------------------------------------------------------------------

def _model_path(self, name: str) -> Path:
return self._models_dir / f"{_sanitize(name)}.json"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prevent filename collisions in JSON model storage

Model files are keyed by _sanitize(name), which replaces /, \\, and whitespace with _. Distinct model names can therefore map to the same filename (for example "a/b" and "a b"), causing one save to overwrite another and making reads by name/hash inconsistent. This is data-loss behavior in the new JSON backend.

Useful? React with 👍 / 👎.

Comment on lines +159 to +160
since_dt = datetime.fromisoformat(since) if since else None
until_dt = datetime.fromisoformat(until) if until else None
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle invalid changelog date params as client errors

changelog_endpoint parses since/until with datetime.fromisoformat directly from query strings. Malformed values raise ValueError and currently bubble as 500 responses instead of a 4xx validation error, so bad client input is reported as a server failure.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant