AGENTS.md 4.0 KB

MCP Gateway Engineering Guide

Ownership

This repository owns the Python MCP Gateway boundary:

  • MCP tool names, descriptions, input Schema, local/public registration, and HTTP route mapping.
  • MCP protocol adaptation, safe display DTOs, request/session forwarding, and Gateway operations.
  • The cross-repository integration entry point and operator-facing runbook.

Business ownership remains outside this repository:

  • Y:/fmsoperate: tool routes, Validate/Logic/Model, registry state, business permissions, data scope, exports, and access logs.
  • Y:/base: employee identity, Gateway device sessions, MCP token lifecycle, and authentication decisions.
  • Y:/home: employee UI and thin proxy only.
  • Y:/settlement_tests: cross-repository PHP contract tests.

Other repositories should link to this repository's README and project docs instead of copying the Gateway contract.

Hard Boundaries

  • Keep the Gateway thin. It must not query business databases or reproduce logistics rules.
  • Never decide employee, company, menu, or data permissions in Python. Forward the authenticated context to ThinkPHP.
  • Never accept caller-supplied employee, company, role, permission, or internal identity overrides.
  • Public requests use a GWS_xxx device credential to load a Redis Gateway session and its server-side mcp_token.
  • Never log tokens, full Gateway credentials, cookies, authorization headers, or raw backend responses.
  • Local and public tool registries must stay identical. Actual visibility is the intersection with the enabled-tool list returned by fmsoperate; failures close access.
  • query_order is the legacy display exception. All other registered tools use services/output_presenter.py and must fail closed on unknown fields, malformed responses, or unknown errors.
  • initialize and tools/list are not rate limited. tools/call is limited per authenticated device session and tool.
  • The retired authorization-code binding flow must not return to the runtime without a new security review.

Documentation Routes

  • README.md: current tool catalog, routes, configuration, operations, and troubleshooting.
  • project-docs/overview.md: current system summary and cross-repository ownership map.
  • project-docs/requirements.md: current protocol, security, and output contracts.
  • project-docs/tech-specs.md: architecture, session, Presenter, logging, and rate-limit details.
  • project-docs/user-structure.md: user, request, and developer flows plus directory map.
  • project-docs/timeline.md: short milestones and current operational handoff only.
  • docs/superpowers/specs/: dated design decisions and ADR-like context.

The following are not authoritative and are not read by default:

  • .planning/**: temporary session state; archive or remove from the active workspace after delivery.
  • Completed docs/superpowers/plans/**: execution history, not the current contract. When a temporary plan finishes, promote durable facts into README/project docs, record the milestone once, then retire the plan. Do not keep completed checklists as active instructions.

Change Rules

  • Update tool metadata, both registries, CLI forwarding, Presenter mapping, and tests together when adding or changing a tool.
  • Keep tool selection zero-inference: ambiguous result intent or number type requires a user question before any call.
  • Keep request_id across Gateway, PHP, responses, and logs; public ingress generates the trusted trace ID.
  • Changes that affect fmsoperate or base contracts require matching changes and tests in those owning repositories.
  • Do not run registry SQL or restart a shared Gateway process unless the task explicitly authorizes that operational action.

Verification

Run from Y:/mcp:

python -m unittest discover -s tests -p "test_*.py"
python -m coverage run -m unittest discover -s tests -p "test_*.py"
python -m coverage report -m --fail-under=100
git diff --check

Verify the code registry without relying on documentation:

python -c "from app import GatewayApp; print('\n'.join(GatewayApp().registered_tool_names()))"