waynestate / advisor
Wayne State's own MCP server: orients a developer and gives their coding agent read-only tools into every project of the app stack.
Requires
- php: ^8.3
- ext-fileinfo: *
- ext-json: *
- ext-mbstring: *
- composer/semver: ^3.0
- mcp/sdk: 0.9.0
- opis/json-schema: ^2.4
- psr/log: ^3.0
- symfony/console: ^7.4
- symfony/process: ^7.4
Requires (Dev)
- laravel/mcp: 1.1.2
- laravel/pint: ^1.32
- orchestra/testbench: ^10.0
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
- waynestate/pinstripe: ^0.1
Suggests
- laravel/mcp: For the Laravel adapter (1.1.2): ADVISOR then runs beside Laravel Boost with `php artisan mcp:start advisor`
- waynestate/playbook: WSU's shared values; private, git.wayne.edu
Provides
None
Conflicts
None
Replaces
None
README
ADVISOR
Front page: site/index.html, a static field map of the stack (open it in a browser).
On Citadel it is served by the raw-PHP deploy layer in deploy/: a Squire kit whose front controller serves site/ behind HELMET's security headers. For local development, keep serving site/ directly.
Checks: composer ci (the same command runs on GitLab and GitHub).
Documentation: the full docs live at site/docs/index.html, built from docs/ by the shared docs builder (composer docs).
Where the code lives
The canonical repository is on nougat: enterprise-apps/advisor. git.wayne.edu and GitHub (waynestate/advisor) are read-only mirrors of it. Open issues and merge requests on nougat, not on the mirrors. This moves to git.wayne.edu once it has CI runners. Our advice: start at nougat.
Every WSU student has an advisor. Now every WSU app does too, and this one answers email.
ADVISOR is an MCP server for the Wayne State application stack, a bit like Laravel Boost but for WSU. It orients a developer who is new to the stack and gives their coding agent nine read-only tools into it: the CREED standards and policy, PROCTOR results, the upgrade path, the WayneUI markup contract and migration guide, SESAME and HELMET guidance, and the parts list. Ask it "which PHP can I build on?" and it answers from the policy file, with the date and the source, instead of from whatever the model remembers.
It is read-only in 0.1. It cannot write a file, run a shell command, or call out to the network. The two things it
can start are the app's own vendor/bin/proctor and Booster's read-only plan, both with a fixed argument shape.
Where it sits in WELD
The stack is the Wayne State application stack. WELD, Wired, Everything, Linked, Deployed, is its Laravel starter app. You can add ADVISOR to a WELD app or to any other Wayne State PHP app, and in either case it is the part of the stack that talks to your coding agent.
| Part | What it is |
|---|---|
| WayneUI | interface (project 0) |
| CREED | version policy and written standards |
| PROCTOR | checks an app |
| SESAME | sign-in inside the app |
| HELMET | runtime baseline |
| Sourdough | starter |
| Preflight | CI components |
| Pinstripe | coding standard |
| Booster | upgrades |
| Rollcall | fleet census |
| Drawbridge | sign-in gateway |
| Squire | deploy kit |
| ADVISOR | MCP server |
| WELD | the Laravel starter app |
ADVISOR serves a bundled copy of what these projects publish (see "Served content"), so it works offline, and it does not replace Laravel Boost. Boost knows Laravel; ADVISOR knows WSU. Run both.
Install
PHP 8.3 or newer (8.3, 8.4 and 8.5 are tested). Add it to the app as a dev dependency:
composer require --dev waynestate/advisor
The command is then php vendor/bin/advisor. This works the same on Windows, macOS and Linux, because it is one PHP
command and not a shell script.
ADVISOR is on Packagist: composer require --dev waynestate/advisor:^0.1. No extra repository needed.
Quick start
-
Print the config your MCP client needs, and read it:
php vendor/bin/advisor install --client=claude -
Write it into the project when you are happy with it:
php vendor/bin/advisor install --client=claude --write -
Open the project in your client and approve the
advisorserver by name when it asks.
The command it registers is always the same fixed string, php vendor/bin/advisor mcp, with ADVISOR_ENABLED=1 in
its environment. Clients: claude (.mcp.json), warriorgpt (.mcp.json), cursor (.cursor/mcp.json), vscode
(.vscode/mcp.json) and codex (TOML, printed only; paste it into ~/.codex/config.toml).
One line for Claude Code
If you would rather type it than generate it, this does the same as install --client=claude --write; project scope
keeps it out of your other projects, and the same "do not commit it" warning applies:
claude mcp add --scope project advisor -e ADVISOR_ENABLED=1 -- php vendor/bin/advisor mcp
No PHP on your PATH? Use Docker
If PHP is not on your PATH, point the client at Docker instead. Run it from the project root, the folder that holds
vendor/, with a hand-written .mcp.json like this:
{
"mcpServers": {
"advisor": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"--network",
"none",
"-v",
".:/app:ro",
"-w",
"/app",
"-e",
"ADVISOR_ENABLED=1",
"-e",
"ADVISOR_PROJECT_ROOT=/app",
"-e",
"ADVISOR_GROUPS=stack",
"-e",
"ADVISOR_READ_ONLY=1",
"composer:2.10.3",
"php",
"vendor/bin/advisor",
"mcp"
]
}
}
}
The container has no network, and the project is mounted read-only. ADVISOR_GROUPS=stack keeps the tools that start
programs (proctor, upgrade_path) off, because those need the app's own PHP. Some clients want an absolute path in
place of . for the mount; give them one. Advice comes in; nothing goes out. This file is written by hand, so keep it
out of git like the generated one.
install never replaces a different existing advisor entry unless you pass --force, keeps your other servers, and
warns when the file is not in .gitignore. A client config starts a program on whoever opens the project, so do not
commit a generated one; this repository ignores .mcp.json for that reason.
Run the client from the folder that holds vendor/. If it starts somewhere else, set ADVISOR_PROJECT_ROOT.
Laravel apps
ADVISOR also ships a Laravel adapter on laravel/mcp, auto-discovered by the service provider. With it, the same
server runs as php artisan mcp:start advisor, beside Boost. Publish the config if you want it in the app:
php artisan vendor:publish --tag=advisor-config
Settings come from the same ADVISOR_* variables, read through config/advisor.php. The same rules apply: off unless
ADVISOR_ENABLED is set, and never in production.
What it offers
Nine tools, all read-only. Each one says where its answer came from:
| Tool | Does |
|---|---|
list_projects |
The stack's projects, WayneUI first, with built or proposed state, package and version. |
get_project_status |
One project: state, package, version, PHP constraint, its floor under today's policy, next step. |
read_project_docs |
A project's README, best practices, or (WayneUI only) markup contract and 1.x to 2.x migration guide, paged. |
policy_lookup |
Support state for a runtime or tool (and an optional version) from CREED, as of today. |
list_stack_rules |
CREED handbook rules, each with its file and line, filtered by app permutation and chapter. |
owner_of |
Answers "which project owns X?" from the bundled ownership table, with a fuzzy topic match. |
playbook_lookup |
A shared value by key, or every key: an environment variable of the same name wins, then Playbook; secret-looking keys are refused. |
run_proctor_check |
Runs the app's own vendor/bin/proctor check against the bundled policy and returns the findings. |
upgrade_path |
Plans the app's PHP and/or Laravel upgrade, one supported hop at a time, via Booster's read-only plan. |
The groups split by what they may start: stack never starts a program, proctor starts the app's own
vendor/bin/proctor, and booster starts Booster's read-only plan (never run). Set ADVISOR_GROUPS=stack
when you want ADVISOR as pure reference material.
| Resource | Serves |
|---|---|
advisor://policy |
CREED's policy file. |
advisor://creed/<chapter> |
One per handbook chapter. |
advisor://guide/<slug> |
Hand-written guides; the first is "Connect to Oracle on Citadel". |
Prompts: orient_me and plan_upgrade.
Here is what that looks like when you ask. Each numbered control point is one of the tools above, with the arguments the front page's own route table sends:
Everything ADVISOR returns is labeled as reference data. PROCTOR in CI, not ADVISOR, decides pass or fail.
Configuration
ADVISOR reads the process environment only, never a file. The placeholders are in .env.example.
| Variable | Default | Meaning |
|---|---|---|
ADVISOR_ENABLED |
off | Must be 1 or true, or the server refuses to start. advisor install sets it in the client config. |
ADVISOR_ENV |
APP_ENV, then local |
production or prod always refuses to start. |
ADVISOR_PROJECT_ROOT |
the working directory | The folder run_proctor_check may look inside. Paths outside it are refused. |
ADVISOR_READ_ONLY |
on | Set to 0 or false to allow tools that write. None exist in 0.1. Read-only wins over ADVISOR_GROUPS. |
ADVISOR_GROUPS |
all | Comma list of tool groups to offer (stack, proctor, booster). See below for what each group may start. |
ADVISOR_PROCTOR_TIMEOUT |
120 |
Seconds before a PROCTOR run is stopped (1 to 900). |
ADVISOR_BOOSTER_TIMEOUT |
60 |
Seconds before a Booster plan is stopped (1 to 900). A plan is local and fast, so the default is shorter than a PROCTOR run. |
ADVISOR_BOOSTER_BIN |
none | Where upgrade_path finds Booster when the app has no vendor/bin/booster of its own: the path to booster's bin/booster (or a booster.phar). The app's own vendor/bin/booster always wins. |
ADVISOR_MAX_CHARS |
20000 |
Largest piece of text one doc call returns (1000 to 45000). |
ADVISOR_AUDIT_LOG |
off | A file for a one-line-per-call audit log: time, tool, a hash of the arguments, outcome, duration. Arguments are hashed, never written. |
ADVISOR_LOG_LEVEL |
warning |
Log level on stderr (debug to emergency). |
ADVISOR_STACK_DIR |
none | Folder holding the sibling stack repos; used only by content:generate. |
ADVISOR_PRIVATE_DENYLIST |
none | A file (kept outside the repo) with private strings to strip from the bundle; used by content:generate and the public-posture test. See "Releasing". |
ADVISOR_MODULES_FILE |
none | An extra module manifest (see "Modules"). |
A bad value stops the server with a message on stderr. It does not guess.
Commands
php vendor/bin/advisor mcp # serve over stdio (your client starts this)
php vendor/bin/advisor install [--client=claude] [--write] [--force] [--path=.]
php vendor/bin/advisor content:verify # check the bundled content against its manifest
php vendor/bin/advisor content:generate [--sources=<dir>] [--check]
php vendor/bin/advisor stack-parts [--write=<readme>] [--check=<readme>]
php vendor/bin/advisor doctor [--json] # check the install when the server does not show up
Stdout carries only protocol messages. Logs, warnings and stray output go to stderr, so a client never sees garbage.
When it doesn't show up
You added the config, opened the project, and the advisor server is not in the list. Run:
php vendor/bin/advisor doctor
It checks the four things that keep a server from appearing, in plain words, and changes nothing:
- php: the PHP on your machine satisfies ADVISOR's own
composer.json(8.3 or newer). - content: the bundled
data/still matches its manifest. - client:claude, client:warriorgpt, client:cursor, client:vscode: each client's MCP config: is the file there, does it have the
advisorentry, and is the script it points at in the project? A missing file is a warning, not a failure; you only need the one client you use. - mcp: the server actually starts. Doctor boots it in-process and asks for its tools.
A fail is something to fix; a warn is something you may not care about. The exit code is 0 when nothing failed and 1 when something did; --json prints the findings as JSON.
The advisor will see you now.
Served content
The handbook, policy, project docs, the WayneUI markup contract and WayneUI's 1.x to 2.x migration guide in data/
are generated from the sibling stack repositories by content:generate and verified at start-up against
data/manifest.json (a SHA-256 for every file). If a file does not match, the server refuses to start rather than
serve it. Files are cleaned on the way in: line endings are normalized and invisible characters (the TAG block,
zero-width and bidirectional controls) are stripped. Each project entry also carries its version when the project
publishes one (composer.json, else package.json, else the newest CHANGELOG heading).
Beside the generated docs, hand-written guides are kept by hand in generator/guides/ (one Markdown file per slug),
the same way the ownership table is kept. Each is bundled into data/guides/ and served as advisor://guide/<slug>;
the first is "Connect to Oracle on Citadel".
To refresh it, with the stack repos checked out side by side:
php vendor/bin/advisor content:generate --sources=/path/to/stack
php vendor/bin/advisor content:generate --sources=/path/to/stack --check # exits 1 on drift
CI cannot see the sibling repositories, so composer ci runs content:verify, which needs none of them.
Releasing
ADVISOR is a public repository, so the bundle must not carry internal names. The public-posture test always checks
generic shapes (ADR numbers, open-question numbers, design-doc paths, the coordination repo). Private strings
(hostnames, people) are loaded from the file named by ADVISOR_PRIVATE_DENYLIST and checked only when the file is
present.
To release:
- Set
ADVISOR_PRIVATE_DENYLISTto a file kept outside the repository (one literal orregex:pattern per line,#comments). - In GitLab CI, the file comes from a masked file-type CI variable.
- Regenerate the bundle with the file present:
php vendor/bin/advisor content:generate --sources=/path/to/stack. - Run
composer cito confirm the public-posture test passes.
The file is never committed. The bundle is regenerated with the file present before every release.
Modules
Each stack project can later ship its own ADVISOR tools. Modules load only from a manifest (modules.json), only from
the Waynestate\ namespace, and only if they implement the Module interface. Nothing scans a folder.
Running the checks
Maintainers: how to build, test and release this project, and where its data comes from, is in MAINTAINING.md.
composer ci
That runs composer validate --strict, content:verify, PHPUnit, Pint, PHPStan (level 8, Pinstripe's raw preset,
included from vendor/waynestate/pinstripe) and composer audit. No PHP on your machine? Use Docker:
docker run --rm -v "${PWD}:/app" -w /app composer:2.10.3 sh -c "composer install --no-interaction && composer ci"
Tests drive the real SDK server in-process with a small MCP client, snapshot tools/list, and start
bin/advisor mcp as a subprocess to prove stdout stays clean. To accept an intended change to the tool list, run
ADVISOR_UPDATE_SNAPSHOTS=1 vendor/bin/phpunit, then review and commit the diff.
Safety, in short
- Off unless enabled; refuses production; refuses to start on a bad setting or a content file that fails its hash.
- Read-only by default; no tool takes a command, a URL or a free path.
- Every result is cleaned of invisible characters and labeled as data; secrets in PROCTOR output are redacted.
run_proctor_checkruns one fixed program with an argument array and an allowlisted environment, never a shell.upgrade_pathruns Booster's read-onlyplanthe same way: one fixed program, an argument array, an allowlisted command, never a shell. It plans; it never runs a hop.- No network calls, and no model calls, from ADVISOR itself.
See BEST_PRACTICES.md, and OPEN_QUESTIONS.md for what is decided, assumed and not yet built.
Why "ADVISOR"
Because registration is open, the advisor will see you now, and unlike the real one it knows which PHP version you are
allowed to be on. A little Creed energy too: it will tell you who you are, in composer.json, whether you like it or not.
License
MIT. Copyright Wayne State University.
Everyone on the sheetWayneUI and the stack. ADVISOR is the one with office hours.
scripts/readme-art. No appointment necessary.

