Skip to content
mcp-data-platform composable mcp data platform
v1.x part of txn2 ↗

Admin API

The Admin REST API provides HTTP endpoints for managing knowledge insights and changesets, and for listing every user's memory records, outside the MCP protocol. Use it for building dashboards, integrating with existing governance tools, or scripting batch operations.

Authentication

All endpoints require admin authentication via API key. Pass the key as either:

  • X-API-Key: <key> header
  • Authorization: Bearer <key> header

The key must resolve to a user with the admin role. Requests without valid credentials receive 401 Unauthorized. Requests with a valid key but no admin role receive 403 Forbidden.

Insight Endpoints

List Insights

GET /api/v1/admin/knowledge/insights

Returns a paginated list of insights with optional filtering.

Query Parameters:

Parameter Type Description
status string Filter by status: pending, approved, rejected, applied, superseded
category string Filter by category: correction, business_context, data_quality, usage_guidance, relationship, enhancement
entity_urn string Filter by related entity URN
captured_by string Filter by the user who captured the insight
confidence string Filter by confidence level: high, medium, low
source string Filter by source: user, agent_discovery, enrichment_gap
since RFC 3339 Filter insights created after this timestamp
until RFC 3339 Filter insights created before this timestamp
page integer Page number, 1-based (default: 1)
per_page integer Results per page (default: 20, max: 100)

Example:

curl -s "https://mcp.example.com/api/v1/admin/knowledge/insights?status=pending&category=correction" \
  -H "Authorization: Bearer $ADMIN_API_KEY" | jq

Response:

{
  "data": [
    {
      "id": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
      "created_at": "2025-01-15T14:30:00Z",
      "session_id": "sess_abc123",
      "captured_by": "[email protected]",
      "persona": "analyst",
      "source": "user",
      "category": "correction",
      "insight_text": "The amount column represents gross margin before returns, not revenue.",
      "confidence": "high",
      "entity_urns": [
        "mcp:dataset:(trino,warehouse,hive.sales.orders)"
      ],
      "related_columns": [],
      "suggested_actions": [
        {
          "action_type": "update_description",
          "target": "amount",
          "detail": "Gross margin before returns"
        }
      ],
      "status": "pending"
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 20
}

Get Insight

An insight's detail drawer in the portalAn insight's detail drawer in the portal

The portal's review drawer shows the same insight; the fields below are what this endpoint returns for it.

GET /api/v1/admin/knowledge/insights/{id}

Returns a single insight by ID.

Example:

curl -s "https://mcp.example.com/api/v1/admin/knowledge/insights/a1b2c3d4e5f67890a1b2c3d4e5f67890" \
  -H "Authorization: Bearer $ADMIN_API_KEY" | jq

Response:

{
  "id": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
  "created_at": "2026-01-15T14:30:00Z",
  "session_id": "sess_abc123",
  "captured_by": "[email protected]",
  "persona": "analyst",
  "source": "user",
  "category": "correction",
  "insight_text": "The amount column represents gross margin before returns, not revenue.",
  "confidence": "high",
  "entity_urns": [
    "mcp:dataset:(trino,warehouse,hive.sales.orders)"
  ],
  "related_columns": [
    {
      "urn": "mcp:dataset:(trino,warehouse,hive.sales.orders)",
      "column": "amount",
      "relevance": "direct"
    }
  ],
  "suggested_actions": [
    {
      "action_type": "update_description",
      "target": "amount",
      "detail": "Gross margin before returns"
    }
  ],
  "status": "pending",
  "observed_entities": [
    {
      "urn": "mcp:dataset:(trino,warehouse,hive.sales.orders)",
      "query_table": "hive.sales.orders",
      "connection": "primary",
      "estimated_rows": 1200
    }
  ]
}

Observed Warehouse State

A pending insight is a claim awaiting certification, and some claims are about entities the platform can query itself. When a pending insight's entity URN resolves through the configured query provider to an available table, the list and detail payloads carry that observation beside the claim in observed_entities, one entry per resolved URN, in the order the insight carries them.

Field Type Description
urn string The entity URN this observation is for
query_table string What the entity is queryable as, e.g. hive.sales.orders
connection string The query connection the table lives on
estimated_rows integer Rows the engine currently estimates. Present only when enrichment.estimate_row_counts is on (Configuration)
conflict object Advisory marker: claimed_rows, observed_rows, and a message naming both

conflict is raised when the claim text states an integer and the table estimates a different one. It is advisory: an estimate is an estimate, the number in the claim may be about something else, and promotion is never refused mechanically — approve and reject behave exactly as they do without it. When a claim states several numbers, the one nearest the estimate is compared, since if even that number disagrees with the warehouse, every reading of the claim does.

observed_entities is omitted, not empty, whenever there is nothing observed:

  • the insight is not pending (a decided insight is history, not a decision to inform)
  • the URN does not resolve, or the provider reports the table unavailable
  • the query provider is absent or noop
  • the lookups did not finish inside the read path's budget

In every one of those cases the payload is byte-identical to what the endpoint served before it could observe anything, so a client that does not know the field is unaffected.

Update Insight

PUT /api/v1/admin/knowledge/insights/{id}

Update the text, category, or confidence of an insight. Only pending insights can be edited.

Request Body:

{
  "insight_text": "Updated description of the insight",
  "category": "business_context",
  "confidence": "high"
}

All fields are optional. Only provided fields are updated.

Example:

curl -X PUT "https://mcp.example.com/api/v1/admin/knowledge/insights/a1b2c3d4e5f67890" \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confidence": "high"}'

Response: Returns the updated insight (same structure as Get Insight).

Update Insight Status

PUT /api/v1/admin/knowledge/insights/{id}/status

Approve or reject an insight.

Request Body:

{
  "status": "approved",
  "review_notes": "Verified with data engineering team"
}
Field Type Required Description
status string Yes Target status: approved or rejected
review_notes string No Notes explaining the decision

Valid Transitions:

From To
pending approved
pending rejected
pending superseded

Example:

curl -X PUT "https://mcp.example.com/api/v1/admin/knowledge/insights/a1b2c3d4e5f67890/status" \
  -H "Authorization: Bearer $ADMIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "approved", "review_notes": "Confirmed by data team"}'

Response:

{
  "id": "a1b2c3d4e5f67890a1b2c3d4e5f67890",
  "status": "approved",
  "reviewed_by": "[email protected]",
  "reviewed_at": "2026-04-15T16:00:00Z",
  "review_notes": "Confirmed by data team"
}

Get Insight Statistics

GET /api/v1/admin/knowledge/insights/stats

Returns aggregate statistics about insights.

Example:

curl -s "https://mcp.example.com/api/v1/admin/knowledge/insights/stats" \
  -H "Authorization: Bearer $ADMIN_API_KEY" | jq

Response:

{
  "total_pending": 7,
  "by_entity": [
    {
      "entity_urn": "mcp:dataset:(trino,warehouse,hive.sales.orders)",
      "count": 3,
      "categories": ["correction", "business_context"],
      "latest_at": "2025-01-15T14:30:00Z"
    }
  ],
  "by_category": {
    "correction": 5,
    "business_context": 3,
    "data_quality": 2
  },
  "by_confidence": {
    "high": 4,
    "medium": 5,
    "low": 1
  },
  "by_status": {
    "pending": 7,
    "approved": 3,
    "applied": 12,
    "rejected": 2
  }
}

Changeset Endpoints

List Changesets

GET /api/v1/admin/knowledge/changesets

Returns a paginated list of changesets with optional filtering.

Query Parameters:

Parameter Type Description
entity_urn string Filter by target entity URN
applied_by string Filter by the user who applied the changes
rolled_back boolean Filter by rollback status (true or false)
since RFC 3339 Filter changesets created after this timestamp
until RFC 3339 Filter changesets created before this timestamp
page integer Page number, 1-based (default: 1)
per_page integer Results per page (default: 20, max: 100)

Example:

curl -s "https://mcp.example.com/api/v1/admin/knowledge/changesets?rolled_back=false" \
  -H "Authorization: Bearer $ADMIN_API_KEY" | jq

Response:

{
  "changesets": [
    {
      "id": "cs_x1y2z3a4b5c6d7e8f9a0b1c2d3e4f5a6",
      "created_at": "2025-01-15T16:00:00Z",
      "target_urn": "mcp:dataset:(trino,warehouse,hive.sales.orders)",
      "change_type": "update_description",
      "previous_value": {
        "description": "Order records",
        "tags": ["financial"],
        "glossary_terms": [],
        "owners": ["Data Platform Team"]
      },
      "new_value": {
        "change_0": {
          "change_type": "update_description",
          "target": "entity",
          "detail": "Order records with gross margin amounts (before returns)"
        }
      },
      "source_insight_ids": ["a1b2c3d4e5f67890"],
      "applied_by": "[email protected]",
      "rolled_back": false
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 20
}

Get Changeset

GET /api/v1/admin/knowledge/changesets/{id}

Returns a single changeset by ID.

Example:

curl -s "https://mcp.example.com/api/v1/admin/knowledge/changesets/cs_x1y2z3a4b5c6" \
  -H "Authorization: Bearer $ADMIN_API_KEY" | jq

Response: Same structure as a single item from the list response.

{
  "id": "cs_x1y2z3a4b5c6d7e8f9a0b1c2d3e4f5a6",
  "created_at": "2026-01-15T16:00:00Z",
  "target_urn": "mcp:dataset:(trino,warehouse,hive.sales.orders)",
  "change_type": "update_description",
  "previous_value": {
    "description": "Order records",
    "tags": ["financial"],
    "glossary_terms": [],
    "owners": ["Data Platform Team"]
  },
  "new_value": {
    "change_0": {
      "change_type": "update_description",
      "target": "entity",
      "detail": "Order records with gross margin amounts (before returns)"
    }
  },
  "source_insight_ids": ["a1b2c3d4e5f67890"],
  "approved_by": "[email protected]",
  "applied_by": "[email protected]",
  "rolled_back": false
}

Rollback Changeset

POST /api/v1/admin/knowledge/changesets/{id}/rollback

Restores every catalog aspect the changeset wrote to the value it replaced, in one transaction, returns the changeset's source insights to the review queue as pending, and marks the changeset rolled back. Uses the same revert engine as the apply_knowledge MCP rollback action; see Governance: Rollback for the full semantics.

Example:

curl -X POST "https://mcp.example.com/api/v1/admin/knowledge/changesets/cs_x1y2z3a4b5c6/rollback" \
  -H "Authorization: Bearer $ADMIN_API_KEY"

Response:

{
  "changeset_id": "cs_x1y2z3a4b5c6",
  "target_urn": "mcp:dataset:(trino,warehouse,hive.sales.orders)",
  "reverted_changes": ["restored glossary_terms of mcp:dataset:(trino,warehouse,hive.sales.orders)"],
  "skipped_changes": [],
  "insights_returned_to_review": ["a1b2c3d4e5f6"],
  "rolled_back_by": "[email protected]"
}

Status codes:

Status Condition
200 Reverted and recorded.
404 Changeset not found.
409 Already rolled back; an aspect it wrote has been written again since; or it was written before the platform catalog held its entities, so its writes are not in the catalog's history.
422 The changeset created a prompt, which a rollback does not delete.

Memory Record Endpoints

List Memory Records

GET /api/v1/admin/memory/records

Returns every memory record on the deployment, whoever wrote it, newest first. The portal's GET /api/v1/portal/memory/records lists only the caller's own records; this route is how an administrator sees, counts and audits all of them. It takes the portal route's filters and its limit/offset paging, plus created_by to narrow to one author.

Query Parameters:

Parameter Type Description
created_by string Filter by the author's email
dimension string Filter by dimension
sink_class string Filter by sink class
category string Filter by category
status string Filter by status
source string Filter by source
limit integer Results per page (default: 20, max: 100)
offset integer Records to skip (default: 0)

Example:

curl -s "https://mcp.example.com/api/v1/admin/memory/[email protected]&limit=50" \
  -H "Authorization: Bearer $ADMIN_API_KEY" | jq

Response:

{
  "data": [
    {
      "id": "mem_a1b2c3d4e5f6",
      "created_at": "2026-03-18T08:11:08Z",
      "updated_at": "2026-03-18T08:11:08Z",
      "created_by": "[email protected]",
      "persona": "analyst",
      "dimension": "knowledge",
      "content": "The daily_sales table in the retail schema is partitioned by date.",
      "category": "business_context",
      "confidence": "high",
      "source": "user",
      "entity_urns": [],
      "related_columns": [],
      "metadata": {},
      "status": "active"
    }
  ],
  "total": 212,
  "limit": 50,
  "offset": 0
}

The route is registered only when the deployment has a memory store (a database). Like every route here it is behind the admin gate, so a caller without the admin persona is refused.

Error Responses

All endpoints return errors in a consistent format:

{
  "error": "insight not found",
  "code": "NOT_FOUND"
}
HTTP Status Code Description
400 BAD_REQUEST Invalid request parameters or body
401 UNAUTHORIZED Missing or invalid authentication
403 FORBIDDEN Authenticated but not authorized (not admin)
404 NOT_FOUND Resource not found
409 CONFLICT Invalid status transition or already rolled back
500 INTERNAL_ERROR Server error

Database Schema

Knowledge capture uses two PostgreSQL tables, created by migrations 000006, 000007, and 000008.

knowledge_insights

Column Type Description
id TEXT Primary key (cryptographic random hex)
created_at TIMESTAMPTZ When the insight was captured
session_id TEXT MCP session that produced the insight
captured_by TEXT User who shared the knowledge
persona TEXT Active persona at capture time
source TEXT Where the knowledge came from: user, agent_discovery, enrichment_gap
category TEXT Insight category
insight_text TEXT The domain knowledge content
confidence TEXT Confidence level (high, medium, low)
entity_urns JSONB Related catalog references
related_columns JSONB Related columns
suggested_actions JSONB Proposed catalog changes
status TEXT Current lifecycle status
reviewed_by TEXT Who reviewed the insight
reviewed_at TIMESTAMPTZ When it was reviewed
review_notes TEXT Reviewer comments
applied_by TEXT Who applied the insight
applied_at TIMESTAMPTZ When it was applied
changeset_ref TEXT Link to the changeset

knowledge_changesets

Column Type Description
id TEXT Primary key (cryptographic random hex)
created_at TIMESTAMPTZ When changes were applied
target_urn TEXT Catalog entity that was modified
change_type TEXT Type of changes applied
previous_value JSONB Metadata before changes (for rollback)
new_value JSONB Changes applied
source_insight_ids JSONB Insights that produced this changeset
approved_by TEXT Who approved the changes
applied_by TEXT Who applied the changes
rolled_back BOOLEAN Whether changes were reverted
rolled_back_by TEXT Who reverted the changes
rolled_back_at TIMESTAMPTZ When changes were reverted