Skip to main content
Glama
SuneraUdana

MCP Agent Platform MVP

by SuneraUdana

MCP Agent Platform MVP

A deliberately small hybrid monorepo foundation: Fastify owns the public API and MCP gateway boundary, while FastAPI owns authorization policy decisions. PostgreSQL and Redis are provisioned for the next persistence/cache increment.

Layout

  • apps/api: TypeScript Fastify API, agent registry with PostgreSQL and explicit memory fallback, OpenAPI at /docs.

  • services/policy: Python FastAPI policy boundary with explicit in-memory policy rules and secure default-deny.

  • services/tool: deterministic external HTTP tool used by the local showcase.

  • demo/gradio_app.py: Gradio client for the governed invocation flow.

  • packages/contracts/schemas/domain.json: shared source-of-truth JSON Schema for domain contracts.

  • infra/docker-compose.yml: PostgreSQL and Redis.

  • docs/mvp-evaluation.md: evidence-based scope, security, performance, and readiness assessment.

Related MCP server: mcp-permission-server

Local run

cp .env.example .env
npm install
npm run build && npm test
# development fallback (explicit; no database required)
AGENT_REPOSITORY=memory npm run dev:api
# PostgreSQL-backed registry
docker compose -f infra/docker-compose.yml up -d postgres redis
psql "$DATABASE_URL" -f infra/migrations/001_create_agents.sql
psql "$DATABASE_URL" -f infra/migrations/002_create_credentials.sql
psql "$DATABASE_URL" -f infra/migrations/003_create_audit_events.sql
AGENT_REPOSITORY=postgres npm run dev:api
python3 -m venv services/policy/.venv
. services/policy/.venv/bin/activate
pip install -r services/policy/requirements.txt
pytest -q services/policy/tests
uvicorn services.policy.app.main:app --reload --port 8000
# separate shell: start the external demo tool
uvicorn services.tool.app.main:app --reload --port 8010
# separate shell: npm run dev:api
# optional dependencies: docker compose -f infra/docker-compose.yml up -d

API health: http://localhost:3000/health; policy health: http://localhost:8000/health. Fastify POST /v1/authorize delegates to the policy service. It returns 503 when the policy service is unavailable and 504 on timeout. The policy service defaults to deny until a matching rule is configured.

For a local allow rule, start the policy service with a JSON array:

export POLICY_RULES_JSON='[{"policy_id":"demo-weather","effect":"allow","actor_id":"agent-2","tool_id":"weather","action":"invoke","reason":"Demo agent may invoke weather"}]'
uvicorn services.policy.app.main:app --reload --port 8000

Rules are held in process memory for this MVP. POST /v1/policies and GET /v1/policies support controlled demos; these administrative endpoints are not authenticated yet. Rules match exact tool/action values and optionally an exact actor; unmatched requests remain denied.

Test the integrated authorization boundary:

curl -X POST http://localhost:3000/v1/authorize \
  -H "content-type: application/json" \
  -d '{"actorId":"agent-2","toolId":"example-tool","action":"invoke"}'

The API maps its camelCase request to the policy service's snake_case contract and returns the policy AuthorizationDecision.

Agent registry

The registry supports POST /v1/agents, GET /v1/agents, GET /v1/agents/:id, PUT /v1/agents/:id, and POST /v1/agents/:id/deactivate. Create/update requires a non-empty owner, purpose, and riskTier (low, medium, or high). Expired agents are stored as disabled; active records are automatically disabled when they expire. Disabled agents cannot be updated.

The default repository is an in-memory development fallback. PostgreSQL is only selected when AGENT_REPOSITORY=postgres; it fails fast if DATABASE_URL is missing.

JIT credentials

Issue a short-lived credential for an active agent:

curl -X POST http://localhost:3000/v1/agents/agent-2/credentials \
  -H "content-type: application/json" \
  -d '{"scope":["tools:read"],"ttlSeconds":300}'

The response contains the plaintext secret exactly once. Only its SHA-256 hash is persisted; the API never returns it again. TTL defaults to 15 minutes and is capped at one hour. Credentials require a non-empty scope and an active, non-expired agent.

Revoke a credential:

curl -X POST http://localhost:3000/v1/credentials/<credential-id>/revoke

For PostgreSQL, apply infra/migrations/002_create_credentials.sql after the agent migration, then infra/migrations/003_create_audit_events.sql. The memory fallback stores hashed secrets in process memory and is intended only for development; issued credentials disappear on restart.

Audit and protected MCP boundary

Authorization decisions, credential issuance/revocation, and accepted or rejected tool invocations are recorded with a correlation ID. Records include agent/tool/credential IDs, decision, rationale, and safe metadata; plaintext credentials are never written to audit events.

Invoke a tool only with a valid, unexpired, non-revoked credential whose scope contains tool:<toolId> or tools:invoke:

curl -X POST http://localhost:3000/v1/mcp/invoke \
  -H "content-type: application/json" \
  -H "authorization: Bearer <secret-returned-once>" \
  -H "x-correlation-id: demo-invocation-1" \
  -d '{"agentId":"agent-2","toolId":"example-tool","credentialId":"<credential-id>"}'

The boundary executes a configurable HTTP tool transport after credential and policy checks. Set MCP_TOOL_URL to an external MCP-compatible gateway endpoint; the request body contains toolId, action, payload, and correlationId. The response JSON becomes the tool result. If unset, the API uses a deterministic local adapter for development. Both policy denials and downstream tool failures are audited.

Seed a complete demo flow after starting the API and policy service:

npm --workspace @mcp/api run demo:seed

The command creates an example agent and allow policy, issues a scoped credential, and prints a ready-to-run invocation command. The credential secret is printed once; treat the output as sensitive and revoke the credential after the demo.

For the browser showcase, start the API with MCP_TOOL_URL=http://localhost:8010/v1/tools/invoke, run the seed command, then install the demo dependency and launch Gradio:

pip install -r demo/requirements.txt
DEMO_API_URL=http://localhost:3000 python demo/gradio_app.py

Paste the seed output values into the Gradio form. A successful invocation shows the external tool result and correlation ID. Revoke the credential with POST /v1/credentials/<credential-id>/revoke, then repeat the invocation to demonstrate enforcement.

The final validation commands are:

npm run build && npm test
python3 -m pytest -q services/policy/tests
python3 -m json.tool packages/contracts/schemas/domain.json >/dev/null
docker compose -f infra/docker-compose.yml config >/dev/null

See docs/mvp-evaluation.md for the MVP evaluation, benchmark interpretation, security assessment, readiness decision, and recommended research roadmap.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enforces fine-grained, context-aware access control on MCP tool calls, with a tamper-evident, replayable audit log that records denials and verifies every decision.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to safely call enterprise tools through a governed MCP gateway with permission enforcement, blast-radius controls, input validation, and a full audit trail for every invocation.
    MIT