Cargo.toml(root): Workspace manifest; shared deps and profiles.crates/: Core crates (server, client, protocol alias + 2025-11-25 spec, builders, derive, json-rpc, session-storage, http server, AWS Lambda transport).examples/: Runnable servers/clients showing patterns and real apps.tests/: Integration tests (Tokio async): compliance, session, framework integration.docs/: Architecture/spec notes (see README for overview).
turul-mcp-server: High-level server builder and areas (tools/resources/prompts/etc.).turul-mcp-client: HTTP client library.turul-mcp-protocol: Current-spec alias that re-exports the active protocol crate for downstreams.turul-mcp-protocol-2025-11-25: MCP spec types and contracts for the 2025-11-25 schema.turul-mcp-session-storage: Pluggable session backends (in-memory, SQLite, Postgres, DynamoDB).turul-mcp-json-rpc-server: JSON-RPC 2.0 foundation.turul-http-mcp-server: HTTP/SSE transport.turul-mcp-aws-lambda: AWS Lambda entrypoint integration for serverless deployments.turul-mcp-derive/turul-mcp-builders: Macros and builders for ergonomics.examples/middleware-*/: Reference middleware servers (HTTP + Lambda auth/logging/rate limiting).
- Prefer
turul_mcp_server::McpServer::builder()for integrated HTTP transport; choose function macros, derive macros, builders, or manual traits depending on ergonomics. - Custom transports (Hyper/AWS Lambda/etc.) should construct an
McpServerconfiguration and pass it toturul-http-mcp-serverorturul-mcp-aws-lambda. - Handlers must return domain errors: derive
thiserror::Errorfor new error types and implementturul_mcp_json_rpc_server::r#async::ToJsonRpcError; avoid creatingJsonRpcErrordirectly. - Register additional JSON-RPC methods via
JsonRpcDispatcher<McpError>(or your custom error type) to guarantee type-safe conversion to protocol errors. - Always advertise only the capabilities actually wired (e.g., leave
resources.listChanged=falsewhen notifications are not emitted) and back responses with cursor-aware pagination helpers fromturul_mcp_protocol. - Middleware:
- Attach request/response middleware via
.middleware(Arc<dyn McpMiddleware>)on bothMcpServer::builder()andLambdaMcpServerBuilder. - Middleware executes FIFO before dispatch and reverse order after dispatch.
- Use
StorageBackedSessionView+SessionInjectionto read/write session state safely. - See
examples/middleware-auth-server,middleware-logging-server, andmiddleware-auth-lambdafor working patterns (API-key auth, logging, rate limiting).
- Attach request/response middleware via
- Use
turul_mcp_client::McpClientBuilderwith an appropriate transport (HttpTransport,SseTransport, etc.); the builder owns connection retries and timeouts. - Invoke
client.connect().await?to perform the JSON-RPC handshake; the client automatically sendsinitializeand the requirednotifications/initializedfollow-up. - Interact through the high-level APIs (
list_tools,call_tool,list_resources,read_resource,list_prompts,get_prompt, etc.) which all returnMcpClientResult<T>with richMcpClientErrorvariants. - For streaming notifications, subscribe through the transport-specific stream helpers and always handle progress tokens echoed by tools.
- When embedding in other applications, propagate errors using the typed enums rather than string matching and surface meaningful diagnostics (e.g., include
McpClientError::Lifecyclemessaging when initialization fails).
- Build:
cargo build --workspace - Test (all):
cargo test --workspace - Compliance tests:
cargo test --test mcp_compliance_tests - Lint:
cargo clippy --workspace --all-targets -- -D warnings - Format:
cargo fmt --all -- --check• Fix:cargo fmt --all - Run example:
cd examples/minimal-server && cargo run(adjust folder as needed) - Middleware smoke tests:
bash scripts/test_middleware_live.sh(HTTP) andcargo lambda watch --package middleware-auth-lambda(Lambda) for interactive validation. - Schema/notification regressions:
cargo test --test notification_payload_correctnesscargo test --test mcp_vec_result_schema_testcargo test -p turul-mcp-derive schemars_integration_test
- Target spec: https://modelcontextprotocol.io/specification/2025-11-25
- Requirements: correct JSON-RPC usage,
_metafields, version negotiation, pagination/cursors, progress, and session isolation/TTL. - Validate: run
cargo test --test mcp_compliance_tests; for end‑to‑end session compliance, see README “MCP Session Management Compliance Testing”.
- Shapes must match the latest TS schema in
turul-mcp-protocol-2025-11-25(camelCase, optional_metaon params/results where spec allows). - Prompts:
GetPromptParams.argumentsismap<string,string>at the boundary. Handlers may convert internally toValuefor rendering. - Tools:
ToolSchematype isobject;properties/requiredpresent when needed;annotationsare optional hints. - Resources:
Resource,ResourceTemplate, and results (List*Result,ReadResourceResult) follow TS names, includingnextCursorand_meta. CallToolResult.structuredContentis an optional field in the MCP 2025-11-25 schema. Keep it optional and ensure clients/tests handle its absence correctly.- Tool output schemas:
- External output structs must derive
schemars::JsonSchemaso the derive macros can emit detailed schemas viaschema_for!(T). Missing derives now produce compile-time errors (see CHANGELOG.md v0.2.1 breaking changes). - Zero-config (
outputomitted) heuristics still targetSelf; use#[tool(output = Type)]for accurate schemas on complex responses. - Array outputs (
Vec<T>) are validated bymcp_vec_result_schema_testto ensuretools/listadvertises"type": "array"and the runtime result matches.
- External output structs must derive
- Capabilities: advertise
resources.subscribeandresources.listChangedwhen supported (only setlistChangedwhen wired). - Listing: implement
resources/listandresources/templates/listwith stable, absolute URIs; paginate via cursor (nextCursor). Do not enumerate dynamic template instances inresources/list; publish templates only viaresources/templates/list. - Reading:
resources/readreturnscontents[]withuri,mimeType, and Text/Blob/URI reference; avoidunwrap(). - Dynamic templates: publish via
ResourceTemplate(e.g.,file:///user-{user_id}.json,file:///user-profile-{user_id}.{image_format}); resolve at read-time with strict validation. - Security: enforce roots and access controls (allow/block patterns, MIME allowlist, size caps) for
file://and user input. - Updates: send
notifications/resources/updatedandnotifications/resources/list_changedappropriately. _meta: round-trip optional_metafor list/template operations (params → result meta) to match MCP behavior.- Invalid URIs: do not publish invalid URIs in
resources/list; test invalid cases viaresources/readerror scenarios. URIs must be absolute; encode spaces if demonstrated. - Example:
- List:
curl -s http://127.0.0.1:52950/mcp -H 'Content-Type: application/json' -d '{"method":"resources/list"}' - Read:
curl -s http://127.0.0.1:52950/mcp -H 'Content-Type: application/json' -d '{"method":"resources/read","params":{"uri":"config://app.json"}}'
- List:
- Capabilities: advertise
prompts.listChangedwhen prompts are exposed. - Listing: implement
prompts/listwith stable prompt names; include descriptions. - Retrieval:
prompts/getreturnsmessages[]with roles and text content; definearguments[]withrequiredflags and descriptions. - Meta: support optional
_metaon requests/results; emitnotifications/prompts/list_changedwhen the set changes. - Example:
- List:
curl -s http://127.0.0.1:52950/mcp -H 'Content-Type: application/json' -d '{"method":"prompts/list"}' - Get:
curl -s http://127.0.0.1:52950/mcp -H 'Content-Type: application/json' -d '{"method":"prompts/get","params":{"name":"code_review","arguments":{"language":"rust"}}}'
- List:
- Listing: implement
tools/listwith stable ordering (sort by name) and support pagination (nextCursor) when applicable. _meta: round-trip optional_metafor list operations.- Calling:
tools/callreturnscontent[]and may includeisError;_metaoptional.structuredContentis an optional schema field and must remain optional in handling.
- Capabilities:
resources.subscribe,resources.listChanged,prompts.listChangedmatch actual support. - Endpoints:
resources/list,resources/read,resources/templates/list,prompts/list,prompts/getimplemented and registered (separate handlers). - Types: request params and results follow protocol (cursor in params;
nextCursorand optional_metain results). - Prompts:
GetPromptParams.argumentsis a map of string→string; handler converts safely from inputs. - Messages:
PromptMessageroles and content blocks conform; no ad‑hoc shapes. - Resources:
ResourceContentvariants includeuriandmimeTypecorrectly; URIs are absolute and stable. - Notifications: method names use spec strings (e.g.,
notifications/resources/list_changed,notifications/prompts/list_changed,notifications/tools/list_changed), while capability keys remain camelCase (e.g.,listChanged). - Pagination: respects
cursorand returnsnextCursorwhen more items exist. - Tests: add/keep coverage for all of the above.
notifications/initialized: in strict lifecycle mode, reject operations until client sendsnotifications/initialized; add E2E to verify gating and acceptance after.notifications/progress: progress updates must includeprogressToken. Add at least one strict E2E that asserts ≥1 progress event and token match with tool response.list_changednotifications (e.g.,notifications/tools/list_changed) for tools/prompts/resources must only be advertised/emitted when dynamic change sources exist; keep capability keylistChanged=falsefor static servers.
- On every initialize E2E, assert capability truthfulness for the static framework:
resources.subscribe=false,tools.listChanged=false,prompts.listChanged=false(and others only when actually wired).
- Start a session‑enabled server (choose backend):
- SQLite (dev):
cargo run --example client-initialise-server -- --port 52950 --storage-backend sqlite --create-tables - DynamoDB (prod):
cargo run --example client-initialise-server -- --port 52950 --storage-backend dynamodb --create-tables - PostgreSQL (enterprise):
cargo run --example client-initialise-server -- --port 52950 --storage-backend postgres - InMemory (fast, no persistence):
cargo run --example client-initialise-server -- --port 52950 --storage-backend inmemory
- SQLite (dev):
- Run the compliance client against it:
RUST_LOG=info cargo run --example session-management-compliance-test -- http://127.0.0.1:52950/mcp
- Explore additional servers/clients for manual testing:
- Servers:
examples/minimal-server,examples/comprehensive-server,examples/notification-server - Clients:
examples/logging-test-client,examples/lambda-mcp-client - Pattern:
cd examples/<name> && cargo run
- Servers:
- Port busy: change
--portor stop the existing process. - DynamoDB: ensure AWS credentials are configured; include
--create-tableson first run. - PostgreSQL/SQLite: defaults are auto-configured; if custom DSNs/paths are needed, set via environment variables supported by storage crates.
- Verbose diagnostics: set
RUST_LOG=debugand re-run the command.
- Rust 2024;
rustfmtdefaults; deny warnings in CI. - Naming:
snake_case(items),CamelCase(types/traits),SCREAMING_SNAKE_CASE(consts). - Errors via
thiserror; avoidunwrap()outside tests. - Logging with
tracing; prefer structured fields and UUID v7 correlation.
- Use
#[tokio::test]for async. Key suites:session_context_macro_tests,framework_integration_tests,mcp_compliance_tests. - Add unit tests under
#[cfg(test)]per crate; keep deterministic and isolated.
- Use
tests/sharedserver manager; do not hardcodecurrent_dirpaths. Discover workspace root dynamically. - Add E2E for
resources/templates/list(pagination, stable ordering,_metaround‑trip). - Add a strict SSE progress test validating at least one progress event and
progressTokenmatch. - Add strict lifecycle E2E gating with
notifications/initialized. - Assert initialize capability snapshot in each E2E suite.
- Commits: imperative subject (≤72 chars), meaningful body; reference issues (
Fixes #123). - Pre‑PR:
cargo fmt,cargo clippy -D warnings,cargo test --workspace; update README/examples/docs when APIs change. - PRs: clear description, linked issues, testing notes (commands/output), risk/rollback.
- Never commit secrets. AWS examples require valid credentials; prefer env vars/roles.
- Keep debug logs off by default; gate experimental features behind flags.
- Scope: this file applies to the entire repository.
- Role: act as a strict critic for MCP 2025-11-25 compliance within the Turul MCP Framework; flag deviations and propose compliant fixes.
- Do not relax security, logging, or API contracts to “make tests pass”; fix root causes while preserving spec compliance.
- Boundaries: do not modify core framework areas unless explicitly requested. The ~9 areas are Tools, Resources, Prompts, Sampling, Completion, Logging, Roots, Elicitation, and Notifications.
- Extensions: if introducing truly non-standard fields, document them clearly, keep optional, and ensure baseline compliance without them.
- Default stance for review-only requests: no code changes unless the user explicitly asks for a patch.
- Review output should prioritize findings over summaries:
- Lead with concrete issues (severity-ordered) and file references.
- Separate architecture risks, spec compliance risks, and documentation/process drift.
- Call out missing tests/coverage when behavior claims change.
- Treat docs/examples/agent-instruction changes as potentially compliance-impacting:
- Flag docs that advertise unsupported capabilities or incorrect defaults.
- Flag examples that imply
listChanged/subscription/progress/lifecycle support without matching implementation/tests. - Flag spec-version drift (must remain aligned to MCP
2025-11-25unless intentionally upgraded everywhere).
- When reviewing client/server API guidance, verify it preserves typed error propagation and truthful capability advertisement.
- Start with
git status --short --branchto identify whether changes are code, docs, tests, or agent/process files. - If changes are primarily docs/agent guidance (e.g.,
README.md,CLAUDE.md,GEMINI.md,.claude/agents/*):- Perform a consistency audit across all agent guidance files and this
AGENTS.md. - Check that MCP terminology, method names, capability keys, and spec date are consistent.
- Check that testing commands and compliance expectations match the current framework guidance in this file.
- Perform a consistency audit across all agent guidance files and this
- If no code changed but behavior claims changed, treat that as a review finding unless the claims are demonstrably accurate.
- Architecture boundaries: examples and docs should not encourage bypassing crate layering (
protocolvsservervs transport crates). - Capability truthfulness: docs/tests must not imply dynamic capabilities when the framework is static by default.
- Lifecycle strictness: guidance must preserve
notifications/initializedgating and correct error mapping semantics. - Pagination/meta/schema accuracy: docs/examples must use
cursor,nextCursor, and optional_metaconsistently with the protocol crate. - Notifications naming: spec method names use snake_case path segments; capability keys remain camelCase.
- Tool error semantics: do not normalize transport/framework errors into fake successful tool payloads.
- When multiple agent instruction files are added/modified alongside
README.md, treat it as a coordination risk:- Watch for conflicting role definitions (critic vs implementer vs docs writer).
- Watch for duplicated but diverging command guidance.
- Prefer this
AGENTS.mdas the compliance authority when conflicts exist, and flag drift explicitly.
- Pagination Compliance:
prompts/list,resources/list, andresources/templates/listnow honor caller-suppliedlimitvalues, clamp to the DoS ceiling, and rejectlimit=0. Preserve this behaviour in future patches and cover regression paths in the relevant handler tests. - Lifecycle Errors: Strict lifecycle flows must continue returning
McpError::SessionErrorfor pre-initialization access. Any refactor that touchesSessionAware*handlers needs to preserve the error mapping to-32031. - Tool Error Propagation: Keep propagating
McpTool::callfailures as directMcpErrorresults. Never re-wrap them as successfulCallToolResult::errorpayloads. - Test Coverage: Maintain the behavioural suites that assert pagination limits, lifecycle enforcement, and error propagation; add cases whenever new branches are introduced.
- Server Teardown Discipline: Use
TestServerManager(with itsdrop-based shutdown) for integration/E2E suites. Avoid manualkillsequences that can leave ports occupied and cascade failures into later tests. - Tool Output Schemas: External output types must derive
schemars::JsonSchema; runschemars_integration_testandmcp_vec_result_schema_testbefore tagging a release to ensure detailed schemas (including arrays) are emitted. - Notification Payloads:
notification_payload_correctness.rsmust stay green—any custom notification should round-trip_metaand payload fields exactly.