# SpecDelta — Signed AI Tool Compatibility Receipts (`analytical_gratefulness/specdelta`) Actor

Detect breaking changes and permission expansion in MCP, OpenAI function-tool, and OpenAPI 3.x schemas. Finds removed operations, newly required fields, type or enum narrowing, tighter bounds, and risky descriptions; returns an Ed25519-signed compatibility receipt via REST or MCP.

- **URL**: https://apify.com/analytical\_gratefulness/specdelta.md
- **Developed by:** [black cow](https://apify.com/analytical_gratefulness) (community)
- **Categories:** AI, Developer tools, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $30.00 / 1,000 spec compatibility receipts

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/platform/actors/running/actors-in-store#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

## SpecDelta

Signed compatibility and safety receipts for MCP, OpenAI tool, and OpenAPI JSON
specifications.

SpecDelta helps an AI agent or CI pipeline answer two questions before it adopts
a tool:

- Is this tool description/schema safe enough to review?
- Did the candidate specification break compatibility or expand permissions?

The core engine is deterministic. It accepts JSON bodies only, never downloads
URLs, never opens local files, and stores hashes and signed receipts instead of
raw specifications.

### Paid operations

| Operation | REST | MCP | Apify event | Price |
| --- | --- | --- | --- | --- |
| `lint_spec` | `POST /v1/spec/lint` | `lint_spec` | `spec-lint` | $0.01 |
| `compare_specs` | `POST /v1/spec/compare` | `compare_specs` | `spec-compare` | $0.03 |

Free endpoints: `GET /capabilities`, `GET /health`, and
`GET /v1/keys/{key_id}`.

### Example

```bash
curl -sS http://127.0.0.1:8000/v1/spec/compare \
  -H 'content-type: application/json' \
  --data @samples/compare_breaking.json
```

Receipts include canonical input hashes, policy hash, status, issues, metrics,
validator version, timestamp, receipt hash, Ed25519 signature, and key id.

### Detection

- Removed operations and newly exposed operations
- Newly required fields, removed properties, type changes, enum narrowing
- Tightened numeric, string, and array bounds
- High-impact operation names and permission expansion
- Prompt-injection-like instructions in tool descriptions
- Invalid or unbounded JSON Schemas
- Excessive specification/context growth
- External `$ref`, `$dynamicRef`, and `$recursiveRef` values

Only local JSON Pointer references are resolved. The service supports a maximum
request size of 2 MiB and does not accept external URLs.

### Local run

```bash
python3.12 -m venv .venv
.venv/bin/pip install -e '.[test]'
.venv/bin/python -m specdelta
```

Open `http://127.0.0.1:8000/docs`. MCP is available at `/mcp/` in local and
Apify modes.

Development uses an ephemeral signing key. Production startup refuses to run
without `SPECDELTA_ED25519_PRIVATE_KEY_B64`.

### Signing key

```bash
.venv/bin/python scripts/generate_signing_key.py
```

The command writes a new mode-0600 secret file under `.secrets/` and prints only
the file path, key id, and public key. It refuses to overwrite an existing file.
Copy the private value into the deployment platform's Secret manager, then keep
or securely delete the local file.

### Apify

The project supports paid standard runs and Standby REST/OpenAPI with `/mcp/`.
Choose `lint-spec` or `compare-specs` in the Input tab to receive the signed
receipt in the run dataset and `OUTPUT` record. Automated Store tests remain
free. Configure Pay-per-event prices exactly as follows:

- `spec-lint`: USD 0.01
- `spec-compare`: USD 0.03

Required Secret: `SPECDELTA_ED25519_PRIVATE_KEY_B64`. Optional ownership
classification Secret: `SPECDELTA_OWNER_APIFY_USER_ID`.

The service persists only receipt/accounting data in its SQLite state and sends
the same non-raw accounting record to the Actor dataset. See
`sales/APIFY_RELEASE.md`.

### x402

Run a separate deployment with `SPECDELTA_BILLING_MODE=x402`. MCP is disabled
there because the payment middleware protects REST routes. Production requires
the exact CDP facilitator origin, an HTTPS public origin, a receiving address,
CDP credentials, and measured costs below each price.

Payment Identifier is required and must equal `idempotency_key`. Base Sepolia
settlements are tests. The first Base mainnet settlement is classified as
`bazaar_indexing`; owner payer hashes are classified as `owner_test`; only later
unrelated mainnet payer hashes count as `external_sale`.

See `sales/X402_RELEASE.md`.

### Verification

```bash
.venv/bin/ruff check src tests scripts
.venv/bin/ruff format --check src tests scripts
.venv/bin/pytest
.venv/bin/pip check
.venv/bin/pip-audit
.venv/bin/python scripts/generate_release_assets.py
.venv/bin/python scripts/release_check.py
```

Security boundaries and retained/outbound fields are documented in
`SECURITY.md`.

### 한국어

SpecDelta는 AI가 사용할 MCP/OpenAI/OpenAPI 도구 명세를 검사하고,
호환성 파괴·권한 확대·위험 설명을 탐지해 Ed25519 서명 영수증을 반환합니다.
URL 다운로드나 로컬 파일 읽기 기능이 없으며, 원본 명세는 저장하지 않습니다.
`lint_spec`은 $0.01, `compare_specs`는 $0.03입니다.

# Actor input Schema

## `mode` (type: `string`):

lint-spec costs $0.01; compare-specs costs $0.03; readiness-check is free.

## `spec` (type: `object,array`):

MCP, OpenAI tool, or OpenAPI specification used by lint-spec.

## `baseline` (type: `object,array`):

Original specification used by compare-specs.

## `candidate` (type: `object,array`):

New specification used by compare-specs.

## `format` (type: `string`):

Choose a format or let SpecDelta detect it from the supplied document.

## `policy` (type: `object`):

Optional risk patterns and compatibility thresholds.

## `idempotency_key` (type: `string`):

Unique request key, 8–128 characters. Reuse only when retrying the same input.

## Actor input object example

```json
{
  "mode": "lint-spec",
  "spec": {
    "tools": [
      {
        "name": "read_order",
        "description": "Read an order by id.",
        "inputSchema": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            }
          },
          "required": [
            "id"
          ],
          "additionalProperties": false
        }
      }
    ]
  },
  "format": "mcp",
  "policy": {},
  "idempotency_key": "store-spec-example-0001"
}
```

# 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 = {
    "mode": "lint-spec",
    "spec": {
        "tools": [
            {
                "name": "read_order",
                "description": "Read an order by id.",
                "inputSchema": {
                    "type": "object",
                    "properties": {
                        "id": {
                            "type": "string"
                        }
                    },
                    "required": [
                        "id"
                    ],
                    "additionalProperties": false
                }
            }
        ]
    },
    "format": "mcp",
    "policy": {},
    "idempotency_key": "store-spec-example-0001"
};

// Run the Actor and wait for it to finish
const run = await client.actor("analytical_gratefulness/specdelta").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 = {
    "mode": "lint-spec",
    "spec": { "tools": [{
                "name": "read_order",
                "description": "Read an order by id.",
                "inputSchema": {
                    "type": "object",
                    "properties": { "id": { "type": "string" } },
                    "required": ["id"],
                    "additionalProperties": False,
                },
            }] },
    "format": "mcp",
    "policy": {},
    "idempotency_key": "store-spec-example-0001",
}

# Run the Actor and wait for it to finish
run = client.actor("analytical_gratefulness/specdelta").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "mode": "lint-spec",
  "spec": {
    "tools": [
      {
        "name": "read_order",
        "description": "Read an order by id.",
        "inputSchema": {
          "type": "object",
          "properties": {
            "id": {
              "type": "string"
            }
          },
          "required": [
            "id"
          ],
          "additionalProperties": false
        }
      }
    ]
  },
  "format": "mcp",
  "policy": {},
  "idempotency_key": "store-spec-example-0001"
}' |
apify call analytical_gratefulness/specdelta --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=analytical_gratefulness/specdelta",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/nrG5PN1Z8De4178jb/builds/zZOHQZYDCderqOGla/openapi.json
