Skip to content

Releases: shakaran/symfony-agent-mcp

v1.1.0 — 820 analyser modules under test, six defects fixed

Choose a tag to compare

@shakaran shakaran released this 24 Aug 17:49

The headline

src/tools/ — 820 analyser modules, the bulk of the codebase — had no tests at all, while being the source of every parsing defect found so far.

It now has a sweep that drives all of them across five situations: a realistic Symfony application, one doing everything the tools exist to flag, a project containing nothing, an empty directory, and a path that does not exist. Each must return something the server can serialise, without throwing and without hanging.

Statement coverage 2.8% → 78.9%. Lines 80.7%. Functions 85.2%. 6,760 tests.

Writing those tests found six real defects

Five analysers could not see a method that declares a return type. The matcher went straight from the closing parenthesis to the brace:

/function\s+buildForm\s*\([^)]+\)\s*\{/

That matches buildForm($builder, $options) { and misses buildForm(FormBuilderInterface $builder, array $options): void { — which is exactly what Symfony's own maker generates. Every form written that way was invisible: no fields, no constraints, no findings.

Three of the five are worse: getGroupSequence(): array and guessType(): ?TypeGuess are the signatures the Symfony interfaces oblige you to write. Those analysers could never report on a correct implementation.

Fixed in forms, symfony-form-data-mapper, symfony-form-type-guesser, symfony-validator-group-sequence, symfony-validator-sequence-provider.

The error sanitiser redacted a bare drive root and grew the message doing it. Its Windows-path pattern ended in *, so A:\ became [PATH] — six characters for three. A drive root names no file and reveals nothing. Found by the property-based suite, not by reading the code.

Supply chain and governance

  • SPDX headers on all 921 source files, checked in CI
  • Verified reproducible build — two clean builds compared byte for byte across 3,432 files, re-checked on every push
  • OpenVEX document, generated from pnpm audit rather than written by hand, shipped with the SBOM
  • Developer Certificate of Origin, with a CI check that every commit in a pull request is signed off by its author
  • MCPB bundle for local installs, and a GitHub Packages mirror
  • Branch protection with six required checks, and the governance documents the project was missing

OpenSSF Baseline levels 1, 2 and 3, and the OpenSSF Best Practices passing badge.

Full changelog: https://github.com/shakaran/symfony-agent-mcp/blob/main/CHANGELOG.md

v1.0.3 — the HTTP transport under test, and a 413 that never arrived

Choose a tag to compare

@shakaran shakaran released this 23 Aug 15:05

Fixed

The HTTP transport never delivered its 413.

When a request body exceeded SYMFONY_MCP_MAX_PAYLOAD_BYTES, readBody called req.destroy() on hitting the limit. That tears down the socket before the handler can write its response, so the client saw a hung-up connection — indistinguishable from a network fault, with no indication that the payload was the problem.

The read now pauses instead, letting TCP backpressure slow the sender, and the socket is closed only after the 413 has been written.

This was found by the first test ever written against that file.

Added

src/transport/http-transport.ts had no tests. It was the only file in the project without any, and the only one reachable from the network. It now has 39, run against a real listener on a real port rather than mocked request objects:

  • Security headers on every response, including the refusals, and HSTS present over TLS and absent over plaintext
  • IP allowlist: exact matches, CIDR ranges, and malformed entries failing closed rather than allowing everyone
  • Payload cap, session-token rejection through header, bearer token and query string, per-IP rate limiting with Retry-After
  • TLS and mutual TLS against a certificate generated for the run
  • A full SSE-plus-POST exchange, from opening the stream to posting a message back to its session

0% → 99.5% of lines, 100% of functions. 1,014 tests in total.

Also ships from the commits since v1.0.2:

  • The MCPB bundle (pnpm run build:mcpb) that Smithery distributes for local installs, replacing a smithery.yaml written against a publishing route retired in September 2025
  • A project icon, carried inside the bundle
  • The attestation fix: the previous release published correctly but the job went red, because the verification step moved the tarball out of the workspace before the attest step looked for it

Full changelog: https://github.com/shakaran/symfony-agent-mcp/blob/main/CHANGELOG.md

v1.0.2 — MCP Registry metadata and server instructions

Choose a tag to compare

@shakaran shakaran released this 23 Aug 02:47

Added

The server now publishes instructions in its initialize response.

With progressive discovery on, tools/list advertises five meta-tools and hides the other 1,672 until a category is activated. A client had no way to learn they existed — directories that introspect the server reported it as a five-tool server. The instructions carry the real tool count, the five meta-tools by name, the SYMFONY_MCP_DYNAMIC_TOOLS=false escape hatch, and the read-only guarantee.

They cost 261 tokens. Advertising every schema up front costs about 154,000:

tools advertised tools/list payload
default 5 2.2 kB (~568 tokens)
SYMFONY_MCP_DYNAMIC_TOOLS=false 1,677 616 kB (~153,981 tokens)

Registry metadataserver.json (validated against the official MCP Registry 2025-12-11 schema), mcpName in package.json, glama.json and smithery.yaml, plus a registry-metadata test suite so a future release cannot bump the version in one file and forget the others.

Fixed

  • symfony-health-endpoint-security scanned almost nothing. The filename check ran continue before the route check, so the HEALTH_PATHS lookup below it could never change the outcome — a health endpoint in a controller not named *health*/*ping*/*status* was skipped entirely. Either signal now qualifies.
  • Prototype pollution in symfony-translation-yaml-lint. The domain map was a plain object, so hasOwnProperty(map, '__proto__') read false, the guarded assignment went through the prototype setter, and the write landed on the map's prototype instead of an own key. Both levels now have a null prototype.
  • doctrine-dbal-driveroptions and php-gd-security each tested the same condition on both sides of an ||.
  • http-cache let a non-array trusted_proxies / trusted_headers through a cast that pretended otherwise.

Changed

  • CodeQL scans GitHub Actions workflows alongside JavaScript/TypeScript, one job per language. Both security panels are at zero open alerts.

This is the first release published from CI, so it is the first to carry an SLSA provenance attestation.

Full changelog: https://github.com/shakaran/symfony-agent-mcp/blob/main/CHANGELOG.md

v1.0.1

Choose a tag to compare

@shakaran shakaran released this 22 Aug 10:04

Fixes three tools that were advertised but could not be called.

Fixed

tools/list offered three tools that no handler answered to. A client showed them to the user, and invoking one returned Unknown tool:

  • get_php_dnf_type_stats had lost the plural that its handler and its implementation (getPhpDnfTypesStats) both use. Renamed to get_php_dnf_types_stats.
  • get_php_hash_algorithm_security_tools and get_php_socket_programming_tools were "list my own definitions" entries that were never implemented. Removed — list_tool_categories and search_tools already do this, across every category rather than one.

Tool count is 1,677 as a result, down from 1,679.

Added

A tool-registry-integrity suite asserting that every advertised tool has a handler, that every handler is either advertised or one of the five discovery meta-tools, and that no name appears twice.

Tool definitions live in each tool module while handlers are registered in server.ts, with nothing tying the two together — which is why this class of mistake stayed invisible until call time. The suite was verified by reintroducing the original typo: it fails and names the offending tool.

Upgrade

npx @shakaran/symfony-agent-mcp@1.0.1

No configuration changes. If you referenced get_php_dnf_type_stats anywhere, use get_php_dnf_types_stats.

v1.0.0

Choose a tag to compare

@shakaran shakaran released this 22 Aug 09:59

First public release. A Model Context Protocol server that gives an AI assistant read-only introspection into a Symfony application.

What it does

Exposes 1,679 tools across 16 categories — routes, services and the DI container, Doctrine entities and mappings, database schema, migrations, composer packages, and logs.

Sending 1,679 schemas on connect would exhaust a client's context window before the developer typed anything, so it doesn't. On connect the client sees five discovery tools: list categories, search, activate, deactivate, and show what's active. The model finds what it needs and activates that category, under a 40,000-token budget enforced per session. Set SYMFONY_MCP_DYNAMIC_TOOLS=false for the legacy behaviour.

Security

Every tool call passes through five layers:

validateToolArgs → guardAppPath → withAudit → tool → sanitizeToolResult

Input validation, app-path guarding with symlink resolution, an encrypted audit log (JSONL or SIEM-ready CEF), DLP scrubbing of the output, and a size cap. There is no write path to the application — that is structural, not a configuration option.

Install

npx @shakaran/symfony-agent-mcp

Works with Claude Code, Claude Desktop, Cursor, VS Code and Windsurf. Requires Node.js 22 or newer.

Known issue

Three tools in this release are advertised but have no handler, so calling them returns Unknown tool: get_php_dnf_type_stats, get_php_hash_algorithm_security_tools and get_php_socket_programming_tools. Fixed in v1.0.1.