# Sentinel: API Schema Drift MCP Server (`daneli/sentinel-schema-drift-mcp`) Actor

MCP tools for agents: infer JSON Schemas from APIs, diff them with severity ranking (breaking / risky / additive), and check live endpoints against saved baselines.

- **URL**: https://apify.com/daneli/sentinel-schema-drift-mcp.md
- **Developed by:** [Daniel Eskander](https://apify.com/daneli) (community)
- **Categories:** MCP servers, Automation, Developer tools
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $30.00 / 1,000 endpoint checks

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Sentinel: API schema drift tools for agents

An MCP server that answers one question agents keep needing mid-task: **"did this API change shape, and will it break me?"**

Sentinel infers JSON Schemas from live responses, diffs them structurally, and ranks every change by what it will do to a consumer:

| Severity | Meaning | Examples |
|---|---|---|
| **breaking** | existing consumers can crash | field removed, type replaced (`number → string`) |
| **risky** | consumers may crash on some payloads | field newly optional, newly nullable, type broadened, new value in a declared `enum` |
| **additive** | safe for existing consumers | new field, type narrowed, any value dropped from an enum, any change to an inferred enum candidate set |

```
2 breaking, 0 risky, 2 additive change(s).
[breaking] data.items[].price changed type number → string;
[breaking] data.items[].sku was removed;
[additive] data.items[].status no longer includes value(s) "archived";
[additive] data.items[].currency is a new field.
```

### Tools

| Tool | What it does | Cost |
|---|---|---|
| `infer_schema(json \| url)` | Infer a JSON Schema — types, required vs optional fields (learned across array items), and small string sets as enum candidates | free |
| `save_baseline(name, schema)` | Store a schema under a name for later comparison | free |
| `diff_schemas(before, after)` | Severity-ranked structural diff; each argument is an inline schema or a saved baseline name | charged per call |
| `check_endpoint(url, baseline)` | Fetch → infer → diff in one call. First call creates the baseline; later calls detect drift | charged per call |

### Connect

This Actor runs in Standby mode and speaks MCP over Streamable HTTP. Point any MCP client at the Actor's Standby URL with `/mcp` appended and your Apify token:

```
https://daneli--sentinel-schema-drift-mcp.apify.actor/mcp?token=<APIFY_TOKEN>
```

Claude Code:

```bash
claude mcp add --transport http sentinel "https://daneli--sentinel-schema-drift-mcp.apify.actor/mcp?token=<APIFY_TOKEN>"
```

Baselines persist in the Actor's key-value store, so a schema saved in one session is still there in the next.

### When an agent calls this

**Before building an integration.** A coding agent is about to write code against a vendor API. It calls `infer_schema(url)` to see the real shape (not the docs' claimed shape), writes the client, then `save_baseline("vendor-orders", schema)` so future sessions can verify nothing moved.

**Gating a deploy.** A CI agent runs `check_endpoint(staging_url, "orders-api")` before promoting a build. Verdict `breaking` fails the pipeline with a machine-readable change list; `additive` passes with a note.

**Watching a dependency.** A scheduled monitoring agent checks three vendor endpoints nightly. The first run saved baselines; every later run is one `check_endpoint` call per endpoint, and the agent only wakes a human when the verdict is `breaking` or `risky` — with the exact paths that changed (`data.items[].price changed type number → string`), not a wall of JSON.

### How it behaves

- Optionality is learned, not guessed: a field is required only if it appeared in every sampled object. One sample of one object means everything looks required — point `infer_schema` at list endpoints for real signal.
- Enum candidates are a guess, so they never gate: a value appearing in a hand-written `enum` is `risky`, but any change to an inferred candidate set is `additive`. Values shaped like identifiers (URLs, timestamps, UUIDs, digests, emails, opaque tokens, numeric strings) are never treated as enum candidates.
- `diff_schemas` also accepts hand-written JSON Schema (the subset `type`, `properties`, `required`, `items`, `enum`), so you can diff a live response against a spec, not just against a saved baseline.

### Security

`infer_schema(url)` and `check_endpoint(url)` fetch caller-supplied URLs, so this Actor guards against SSRF: http/https only, hostnames are DNS-resolved and private/reserved addresses (loopback, RFC 1918, link-local/cloud-metadata, CGNAT, unique-local IPv6) are refused, and redirects are followed manually with the same check on every hop. It only reaches public endpoints — it cannot be pointed at internal infrastructure.

### Limitations (v0.1)

- JSON only. No XML, no multipart, no auth headers on fetches yet.
- The diff understands the schema subset `infer_schema` emits; `anyOf`/`allOf` in foreign schemas are not resolved.
- Baselines live in one key-value store per Actor — fine for one team, not yet multi-tenant.

MIT.

# Actor input Schema

## Actor input object example

```json
{}
```

# Actor output Schema

## `mcpEndpoint` (type: `string`):

Streamable HTTP endpoint for this run. Point an MCP client here with your Apify token.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("daneli/sentinel-schema-drift-mcp").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("daneli/sentinel-schema-drift-mcp").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call daneli/sentinel-schema-drift-mcp --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,daneli/sentinel-schema-drift-mcp"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7RO1ZN14mQ53dV5SP/builds/Y3SQUu1TlX3M7GxsO/openapi.json
