Remote MCP Server Tool-Surface and Permission Change Ledger avatar

Remote MCP Server Tool-Surface and Permission Change Ledger

Pricing

from $10.00 / 1,000 run_starts

Go to Apify Store
Remote MCP Server Tool-Surface and Permission Change Ledger

Remote MCP Server Tool-Surface and Permission Change Ledger

Watch remote MCP servers for tool-surface and permission changes. The Actor reads the declared tool list with anonymous requests, compares it against a stored digest, and reports each added tool, removed tool, changed input schema, changed destructive-act

Pricing

from $10.00 / 1,000 run_starts

Rating

0.0

(0)

Developer

kingii98

kingii98

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Categories

Share

Watch the remote MCP servers that you connect your agents to, and learn the day a server adds a tool, changes a tool input schema, changes a destructive-action hint, or starts to demand authentication. Each of those changes alters what your agent can do, so each of them is a security event, not a parse break.

The Actor answers one question for the platform owner who connects agents to third-party MCP servers: what can my agents do today that they could not do yesterday?

The Actor sends anonymous requests only. It accepts no credential, it uses no browser and no proxy, and it calls no tool. It reads the declared surface with the MCP Streamable HTTP transport: one initialize call, one initialized notification, one tools/list walk, and — when the server declares those capabilities — one resources/list walk and one prompts/list walk. A server that demands OAuth gets up to two unauthenticated .well-known GET requests of RFC 9728, so that the row reports where the authorization lives.

The ledger

The Actor stores one digest of each server's tool surface in the named key-value store mcp-tool-surface-ledger, under the record key that ledger_name names. The named store outlives the run; the run's own default store does not.

  • First run for a server: the Actor writes the baseline and records no change. The row carries ledger_status: "created".
  • Later run: the Actor compares the fresh digest against the stored one. When the digests differ, it walks the two surfaces field by field and writes one change row for each difference. The row carries ledger_status: "compared".
  • A server it could not read: the stored entry is kept unchanged. A stale baseline is better than one that records an error page. The row carries ledger_status: "unavailable".

The record is written once, at the end of the run, so an aborted run leaves the previous ledger intact. Servers that this run did not read keep their entry, and the record holds at most 500 servers.

Change classes

Change classWhat it means for a connected agent
tool_addedThe server offers an action that your agent did not have.
tool_removedAn action your agent relies on is gone.
input_schema_changedThe arguments of a tool changed. A stored prompt or a stored call may now be wrong.
destructive_hint_changedA tool now declares (or stops declaring) that it destroys data.
auth_requirement_changedThe server started or stopped demanding authentication, or stopped allowing the anonymous listing.
read_only_hint_changedA tool lost or gained its read-only promise.
open_world_hint_changedA tool changed whether it reaches outside the server.
tool_description_changedThe text that steers your agent's tool choice changed.
server_version_changedThe server reports a new version.

The first five are the default. Each recorded change is one billed event, so a class that you do not select costs nothing.

Severity. An authentication change is high. A destructive hint that turns on, or that stops being declared, is high; one that turns off is medium. A tool that loses a readOnlyHint: true is high. An added tool, a removed tool, a changed input schema and a changed open-world hint are medium. Everything else is low.

A server that stops the anonymous listing hides every tool at once. That is one authentication event, not one removal for each tool, so the tool diff is suppressed for that run and resumes when the listing is readable again.

Input

FieldTypeDefaultMeaning
serversarray of 1 to 200 URLsthe four public demo serversRemote MCP endpoint URLs that speak the Streamable HTTP transport.
ledger_namestringDEFAULTThe record key of the stored baseline set inside mcp-tool-surface-ledger. Use one name for each connected server fleet.
alert_onarray of change classesthe five classes aboveWhich change classes become a change row.
request_timeout_secondsinteger 2 to 12020Per-request timeout for each MCP call and each metadata request.
concurrencyinteger 1 to 205How many servers are read at the same time.
max_redirectsinteger 0 to 103Redirect hops for each request. Every hop is resolved and checked again.
max_response_bytesinteger 10000 to 50000002000000Per-response body cap.
count_resources_and_promptsbooleantrueAlso call resources/list and prompts/list when the server declares those capabilities.
probe_oauth_metadatabooleantrueSend up to two RFC 9728 .well-known GET requests for a server that demands authentication.
max_server_secondsinteger 5 to 60060Wall-clock budget for one server.
max_run_secondsinteger 30 to 3600900Wall-clock deadline for the whole server list.

Every field has a schema default, so a run with the empty input {} reads the four public demo servers and succeeds.

Safety limits

  • Only http and https URLs, and no credential in a URL.
  • localhost, and any hostname that resolves to a private or reserved address, is refused. Every redirect hop is resolved and checked again before it is followed, and a chain that repeats a URL is stopped.
  • An entry that is not a usable target — a malformed URL, a foreign scheme, a localhost target, a URL with a credential, or a repeat of an earlier URL — becomes one row with the BLOCKED_TARGET reason code. It never stops the run, and it is never charged.
  • At most 200 servers, 500 tools for each server, 5 cursor pages for each list method, and 5 MB for each response body. Each response body is read by a streaming reader that stops at the cap.
  • The request timeout does not bound one server on its own, because one server takes up to 17 requests and each request may take several redirect hops. The worst-case wall clock of a run is therefore
    min(ceil(servers / concurrency) * max_server_seconds, max_run_seconds)
    . With the default fixture that is 55 seconds, well inside the platform's five-minute window.

Output

The dataset holds one summary row, one row for each server, and one row for each recorded change. The record_type field separates them.

Server row (record_type: "server"): server_url, final_url, ok, reason_code, error, http_status, transport, protocol_version, server_name, server_version, anonymous_listing_allowed, auth_required, oauth_protected_resource_metadata_url, oauth_authorization_servers, tools_total, resources_total, prompts_total, tool_digest, previous_tool_digest, digest_changed, tools[] (each with name, description_hash, input_schema_hash, read_only_hint, destructive_hint, open_world_hint), tools_truncated, ledger_status, change_count, first_seen, last_changed, digest_algorithm, bytes_read, response_time_ms and observed_at.

Change row (record_type: "change"): server_url, change_class, tool_name, field, old_value_hash, new_value_hash, old_value, new_value, severity and observed_at.

Summary row (record_type: "summary"): servers_requested, servers_checked, servers_changed, servers_unreachable, servers_requiring_auth, servers_allowing_anonymous_listing, anonymous_listing_share, baselines_created, servers_compared, changes_total, changes_by_class, changes_by_severity, alert_on, ledger_store, ledger_name, digest_algorithm and observed_at.

Reason codes

OK, AUTH_REQUIRED, TRANSPORT_LEGACY_SSE, TRANSPORT_UNSUPPORTED, HTTP_ERROR, RPC_ERROR, NOT_JSON, RESPONSE_TOO_LARGE, TOOLS_LIST_FAILED, TIMEOUT, DNS_FAIL, CONNECT_FAIL, BLOCKED_TARGET, REDIRECT_LOOP and RUN_DEADLINE.

A changed surface, a server that demands OAuth, an unusable target and an unreachable server are all business verdicts: the run reports them in the dataset and in the status message, and the run itself succeeds.

Pricing: pay per event

EventUnitCounted as
run_startone for each runCharged once, before any server work. It pays for the container start and the summary row.
server_checkedone for each server contacted and compared against the ledgerCharged once for each server whose ledger_status is created or compared. A server that could not be reached, and an input entry that is not a usable target, were neither contacted nor compared, so neither is charged.
surface_change_recordedone for each recorded change of a tool or permission fieldCharged once for each change row, that is once for each change_count of every server row.

Before it reads a single server, the run limits the server list to the number of server_checked charges that its maximum total charge still allows. The servers that fall outside that budget are not read, and they keep their stored ledger entry unchanged for the next run.

Repeat use

Tool surfaces change with each vendor release. Run the ledger each day or each week over the whole connected server list, and again before each agent release. The value is the cross-run baseline: the first run creates it, and every later run turns it into a list of events that you can act on.

Local development

uv sync
uv run pytest
uv run ruff check .