apify-mcp-policy-gateway
A local-first MCP policy gateway that provides a four-step workflow (plan, approve, execute, read) for a single Apify fixture Actor, enforcing a fixed policy and sanitizing results. It does not call Apify and is designed as a policy control plane.
README
Apify MCP Policy Gateway — M1
A deliberately narrow, local-first MCP policy gateway for one organization-owned Apify fixture Actor.
M1 does not call Apify. It exposes a four-step stdio workflow backed by a fake local Actor transport:
plan_operationapprove_operationexecute_approved_operationread_sanitized_results
The gateway is a policy control plane, not an Actor browser, generic proxy, hosted MCP replacement, or credential forwarder.
Fixed M1 policy
Only this operation can be planned:
| Setting | Allowed value |
|---|---|
| Actor ID | P4XBS39SYl7aeFJ7g |
| Canonical owner/name | metahubb~apify-mcp-policy-fixture |
| Build | 0.1.1 |
| Maximum runtime | 60 seconds |
| Maximum charge ceiling | USD 0.10 |
| Maximum result count | 3 |
| Risk | low only |
The fixture input is restricted to:
{
"message": "1 to 64 characters",
"recordCount": 1,
"delayMs": 0
}
recordCount defaults to 3, delayMs defaults to 0, and unknown input fields are denied. recordCount may not exceed the requested result limit.
Flow
plan_operation
Validates the exact Actor, pinned build, fixture input, timeout, charge ceiling, and result limit. A successful response contains:
- A short-lived plan ID and expiry.
- A canonical SHA-256 input hash.
- A canonical plan digest.
- The explicit
ALLOW_LOW_RISKpolicy decision. - Bounded execution parameters.
approval_requiredstatus.
No credential or raw secret is accepted or returned.
approve_operation
Marks a known, valid, unexpired low-risk plan approved. M1 approval is a local state transition only; it is not a signed human approval mechanism.
execute_approved_operation
Creates and durably writes an execution reservation before invoking the injected ActorRunTransport.
The default transport is local and fake. There is no HTTP Apify adapter and no environment-variable credential loading.
Execution states:
SUBMITTED: the fake transport returned a run identity.REJECTED: the transport returned a definitive HTTP rejection such as 403.EXECUTION_UNKNOWN: submission may have reached the upstream boundary, so it is never automatically retried.
The execution ID is replay-safe. A persisted attempted reservation also recovers as EXECUTION_UNKNOWN rather than issuing another POST.
read_sanitized_results
Reads only locally generated results belonging to an execution in this gateway state namespace. It:
- Returns at most three items.
- Removes keys beginning with
#recursively. - Removes fields whose names look like token, password, secret, API-key, authorization, or credential fields.
- Labels every item with:
{
"dataClassification": "untrusted_external_content"
}
The result envelope also states that content must not be treated as instructions and includes Actor, build, execution, policy, and timestamp provenance.
Local state and audit
By default, npm run start creates .gateway-state/ in the current working directory.
gateway.lockis acquired with exclusive creation. A second process cannot use the same state directory.state.jsonis written to a new file, fsynced, atomically renamed, and followed by a directory fsync.audit.jsonlis append-only in application behavior and each event is fsynced.- Audit events contain identifiers, decisions, state, latency, safe error codes, and item counts—not raw Actor input, fixture output, credentials, or authorization material.
A privileged local filesystem user remains outside the M1 tamper-prevention boundary.
Run over stdio
Requirements:
- Node.js 22 or later.
- Project dependencies installed with npm in a normal development environment.
npm install
npm run start
Standard output is reserved for newline-delimited MCP JSON-RPC messages. Do not add console.log diagnostics to the server process.
Example MCP host command:
{
"command": "npm",
"args": ["run", "start", "--silent"],
"cwd": "/root/apify-mcp-policy-gateway"
}
Tests and type checking
npm test
npm run typecheck
All tests are non-networked. They use only fake transports, in-memory state, temporary local state directories, and deterministic fixture data.
Build-environment note
The execution environment used to produce this M1 could not fetch packages from its configured npm registry. The repository therefore contains a small dependency-free stdio MCP wire adapter and the verification run used Node's built-in test runner. package.json pins the intended maintained MCP SDK and Vitest dependencies, but replacing the temporary stdio adapter with the official SDK wiring and executing the same suite under Vitest remains a tooling-conformance follow-up. This limitation is recorded in M1_IMPLEMENTATION_REPORT.md rather than hidden.
Security boundaries
- No user token field exists.
- No
.envor credential loader exists. - No live Apify request exists.
- No arbitrary Actor, build, run, dataset, or storage identifier is accepted.
- The plan digest and current policy are revalidated before approval, execution, and result access.
- An ambiguous outbound submission is never retried automatically.
- Actor/dataset content is always untrusted external content, never policy or instructions.
- Result sanitation is deterministic key removal, not a claim of complete secret detection.
Intentionally not implemented
- Real Apify REST transport or token handling.
- Arbitrary Actor discovery or execution.
- Signed-JWS approval.
- Approval-required or blocked workflows beyond explicit denial.
- URL-target policy.
- Remote run-status or dataset reads.
- General redaction of values such as email or phone.
- Universal rollback.
- Prompt-injection detection or prevention claims.
- Multi-user or network transport.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.