Changelog
All notable changes to this repo are documented here.
Format follows Keep a Changelog.
The MCP server (under mcp/) follows Semantic Versioning.
Tool name or schema changes bump the minor version (until 1.0.0); breaking
changes after 1.0.0 will bump the major.
[Unreleased]
Section titled “[Unreleased]”Added - dev-playbook plugin v0.1.0 - one command instead of a setup guide
Section titled “Added - dev-playbook plugin v0.1.0 - one command instead of a setup guide”Reaching the v1.1.0 tool surface still cost a running server, a bearer token
and a hand-written claude mcp add-json. mcp/dashboard/templates/setup.html
exists because that is fiddly enough to need a three-tab guide. A Claude Code
plugin collapses it to one command, and carries two things the MCP surface
cannot: behaviour that applies before any tool is called, and hooks that put a
project’s guardrails in context without the model having to think to ask.
- stdio transport -
uv run server.py --stdio. Reuses the same module-scopedServerand_initialization_options()as the SSE path, so the five tools are identical across transports by construction rather than by being kept in sync; a subprocess test drivesinitialize→tools/listand asserts the two surfaces are the same set. There was no stdio transport before, so a plugin could only point at a running server, never launch one. plugins/dev-playbook/- the plugin:plugin.json,.mcp.json, two skills, two hooks, and a transport selector. Versioned independently of the server (0.1.0against the server’s1.1.0).- Team mode - setting
server_urlswitches the transport selector from launching a local server to runningscripts/sse_bridge.py, which pumps JSON-RPC between Claude Code’s stdio and a shared server’s SSE endpoint. It inspects nothing, so team mode cannot drift from what that server advertises, and its calls still land on the shared dashboard. Tested against a real server on a real socket rather than against mocked transports. .claude-plugin/marketplace.json- a self-hosted marketplace at the repo root, with a relative./plugins/dev-playbooksource, so one repo carries both and there is one version to bump. A validator asserts the two agree.using-standardsskill - the corrected form of.cursor/rules/mcp-project-cwd.mdc:projectis the basename of the workspace directory, never another corpus project, never omitted./dev-playbook:scaffold-standards- walks a repo with no standards: detect languages from the tree, read the catalog, take placeholder values out of the actual source (a Java pack’spackagefrom the real root package), preview withdry_run=true, ask, then write.- SessionStart hook - injects the project’s
core/guardrails.mdas context at session start. Guardrails that arrive after the first edit are guardrails that did not work. - PreToolUse hook on
Write|Edit- the definition of done, once per session, before the first edit. Advisory by default;enforce_standardsswitches it to denying edits in a repo with no standards. Off by default, because installing a plugin should not block anyone by surprise. The advisory context is once per session; the deny is every call, because a gate that closes once is not a gate. - Both hooks are wired in shell form. The plan called for exec form
(
args) on the grounds that shell form rejects${user_config.*}; probingclaude plugin validateshowed exec form is accepted by the manifest and then dropped with “entry ignored at runtime” - the hooks would have silently never fired. Shell form costs nothing here because both scripts readCLAUDE_PLUGIN_OPTION_ENFORCE_STANDARDSfrom the environment rather than interpolating it.scripts/validate_plugin.pyand a test both now assert it. - Both hooks read the standards SQLite directly with stdlib
sqlite3- no MCP round-trip, no dependency on a running server - and exit 0 silently on any failure. A hook must never be why a session is broken. CI asserts they import nothing outside the stdlib. scripts/validate_plugin.py- stdlib manifest, layout and hook checks as the always-runs floor underclaude plugin validate, plus a newpluginCI job that runs both.MCP_EDITOR- names the client in telemetry under--stdio, where there is noUser-Agentto sniff.
mcp/config.tomlshippedpassword = "admin123". Removed: a committed credential ends up in git history and in every clone, and a marketplace listing points strangers at this repo. The password now comes only fromMCP_ADMIN_PASSWORD, the waydocker-compose.ymlalready required. The credential remains in git history - rotating anything that reused it is a separate call.config.tomlalso shippedauth = truewhileREADME.mddocumented the default as false. Reconciled tofalseacrossconfig.toml, theserver.pydocstring and the README..pre-commit-config.yamland.github/workflows/ci.ymlwere not valid YAML and could not be parsed at all - CI had never run. Indentation fixed in both.check-jsonadded (every new manifest is JSON, and a malformed one shows up only as a plugin that silently fails to load), and ruff now coversplugins/as well asmcp/..cursor/rules/mcp-project-cwd.mdcnamed three tools that no longer exist -playbook_start,playbook_get,playbook_findwere renamed in v1.1.0. Corrected, and its claim thatprojectis required on every tool narrowed:playbook_list_templatesdoes not take one.- README claimed 601 tests; it was already 615 before this work, and is 662 after.
Added - v1.1.0 - the MCP tool surface is back, scaffolding-first
Section titled “Added - v1.1.0 - the MCP tool surface is back, scaffolding-first”- Five
playbook_*tools.list_tools()has returned[]since v1.0.0; it now advertisesplaybook_start_task,playbook_get_standard,playbook_find_standards,playbook_list_templatesandplaybook_scaffold_standards. Naming is<verb>_<resource>behind theplaybook_namespace. playbook_scaffold_standardsis the headline. An agent in a repo with no standards can generate a full set from the template packs. It contains no scaffolding logic of its own - it callsscaffold_service.py, the same function the dashboard wizard calls, and a test diffs the two resulting stores to prove they cannot drift.playbook_list_templatesis split out of it deliberately. The pack catalog is discovery data; folding it into the scaffold tool’s description would spend those tokens in every conversation. As its own tool it is a round-trip paid only when something actually needs bootstrapping.dry_run=truerenders the manifest and writes nothing, so an agent can show the user what it would create before it creates it.- Every tool declares MCP annotations (
readOnlyHint,destructiveHint,idempotentHint,openWorldHint). Scaffolding is additive but not idempotent, so a client confirms before calling it. A client that sees no annotations is entitled to assume the worst, so none go out bare. - Every error carries a next move. An unknown language lists the valid ids; a missing placeholder says where in the codebase to find it; an unresolvable ref lists the project’s actual documents; scaffolding over an existing project says the store never merges and points at the read tools.
mcp/tools/module layout. One module per tool, each exportingDEFINITIONS+dispatch;server.pyconcatenates and routes them. Newtools/refs.pyholds the ref grammar andtools/common.pythe shared annotations, argument coercion and Next Calls renderer.
Changed - v1.1.0
Section titled “Changed - v1.1.0”- The
refgrammar is the relative path, plus aliases (guardrails,workflow:bug-fix,gate:...). The v0.8.0 grammar (pattern:x,language:kotlin/testing) addressed a filesystem corpus that no longer exists; storage isstandards_files(project, relative_path). A test asserts every ref the tools print resolves back to the row it names. playbook_start_taskcomposesget.render_reffor its guardrails and workflow bodies, with a byte-identity test. Issue #380 found this exact duplication had crept back twice, because earlier passes merged tool names without merging their renderers.findscores in Python overstore.list_files(). BM25 went with the v1.0.0 cut and a project is ~25 documents; FTS5 is the escalation if corpora grow, not now.metrics._LEGACY_TOOL_MAPretargeted onto the new names, and the v0.8/0.9 three (playbook_start/_get/_find) added as sources. Recorded calls are history and must not be orphaned, so the map only ever grows.[enable] scaffold(default true) inconfig.toml. False hides the write tool and refuses it; the read tools are unaffected. With auth enabled, scaffolding also requires an admin token - with auth disabled every principal isrole="user", so an unconditional admin gate would lock the tool out of the default local config entirely.- Dashboard setup page’s “verify it works” steps now describe the real flow.
- README rewritten around the surface; it had described a tool-less skeleton and
claimed 112 tests.
docker-compose.ymlimage tag corrected from0.8.0. - Tests: 522 → 601.
Added - per-rule help popups in the create-standards wizard
Section titled “Added - per-rule help popups in the create-standards wizard”- Every rule row in the wizard now has a help affordance. The picker asks a
user to accept or reject 290 rules from one compressed sentence each; that
sentence cannot be lengthened, because
_bullet()writes it verbatim into the generated markdown. Rules now also carryhelp:(two to four sentences on what goes wrong without the rule) andexample:(a do/don’t pair), shown in a modal. - Placement is a hover-revealed glyph in a right-hand gutter, invisible until
the row is hovered or focused and pinned visible on touch, so 50+ rows do not
become a wall of icons. It uses
opacityrather thandisplay, so it stays focusable and in the accessibility tree while invisible. - The icon is never dead. With no authored help the modal falls back to the rule’s title, severity, id, source, full body and target document.
- Help never reaches the generated markdown. It is authoring metadata for the
picker only - not merely to save the agent’s context, but because
source_hashprovenance means any change to the generated bullet invalidates every stored document’s hash. A byte-identity test pins this. - Content authored for
base,java,typescriptandpython(161 rules).go,kotlinandrustrun on the fallback and are listed as exempt in_PACKS_WITHOUT_HELP, so a coverage test stops the authored packs regressing.
Changed - per-rule help popups
Section titled “Changed - per-rule help popups”rule_rowin_wizard.htmlis a<div>with an inner<label>rather than a<label>wrapping everything: a<button>inside a<label>activates that label, so a nested help trigger would have toggled the checkbox on every click.- The modal CSS moved from page-scoped
.sd-modal-*instandard_detail.cssto a shared.modal-*component instyle.css, since the wizard does not load that sheet.standard_detail.html/.jsupdated to the new class names. - The wizard’s inline expand/collapse-all script moved into a new
dashboard/static/wizard_rules.jsalongside the dialog behaviour.
Added - Python, Go and Rust template packs
Section titled “Added - Python, Go and Rust template packs”- Three new language packs under
mcp/templates/languages/, authored againstmcp/templates/TEMPLATE_SPEC.mdand matching the breadth of the existing java/kotlin/typescript packs (standards, testing, two patterns, anti-patterns, and contributions into guardrails / definition-of-done / architecture, plus a gate script):- python (3.12) - ruff +
mypy --strict,src/layout and PEP 621, pytest, and the Bandit/OWASP vulnerability classes (pickle,yaml.load,shell=True,eval/exec, f-string SQL). Placeholder:python_package. Gate: format → lint → types → tests. - go (1.23) - Effective Go and the Google Go Style Guide,
consumer-defined interfaces,
%werror wrapping, table-driven tests. Placeholder:module. Gate: gofmt → vet → staticcheck →test -race→ govulncheck. - rust (1.85) - Rust API Guidelines,
clippy::pedantic,thiserror/anyhow,#![forbid(unsafe_code)], async cancellation and lock-across-await rules. Placeholder:crate. Gate: fmt → clippy → test → doc → cargo-deny.
- python (3.12) - ruff +
- The wizard’s template-values step now carries a per-placeholder example
(
python_package,module,cratealongsidepackage). - Tests:
LANGUAGE_IDSgrows to six languages, so the combination matrix covers all 63 selections rather than 7; 242 → 522 tests.
Added - dashboard standards module (post-v1.0.0)
Section titled “Added - dashboard standards module (post-v1.0.0)”- Rebuilt the Standards/Projects dashboard page as a lightweight,
self-contained module:
mcp/standards_scanner.pyreadsstandards/straight off disk with no dependency on the deleted corpus/loader/BM25 index. Adds/dashboard/standardsroutes and templates, a project detail view, and unit tests. MCP_STANDARDS_ROOTis back (defaults to<repo>/standards), and the Docker image bakesstandards/back in.- This does not restore any MCP tool -
playbook_start/playbook_get/playbook_findremain deleted; the scanner only backs the dashboard page. - Tests: 105 → 112.
Removed - BREAKING - v1.0.0 (standards feature deleted from MCP)
Section titled “Removed - BREAKING - v1.0.0 (standards feature deleted from MCP)”- The standards MCP tool surface is gone, code included. Deleted
mcp/tools/(all threeplaybook_*tools),mcp/loader.py,mcp/corpus.py,mcp/search.py,mcp/cache.py,mcp/refs.py,mcp/index_render.py,mcp/quality.py,mcp/quality_rules.py,scripts/validate-rules.py,mcp/dev.py,TEMPLATE.mdandCONTRIBUTING.md. (standards/itself and the dashboard’s corpus-health page were later rebuilt - see “Added” above.) - The MCP server advertises no tools.
list_tools()returns[]; everytools/callreturnsUnknown tool. Dispatch, timing and metrics recording are intact, so a new surface can be added at one place inmcp/server.py. - Dashboard temporarily lost the Standards and Guide pages, the corpus-health
scoring, the project detail view and
POST /dashboard/reload; the Standards page was rebuilt (see “Added” above). Users, tools, searches, activity, setup, tokens and user admin remain. - CI drops the corpus validation job; pre-commit drops the
validate-ruleshook. - Tests: 280 → 105 (the corpus, search, quality, refs and tool suites are gone).
- Version bumped to 1.0.0.
Removed - BREAKING - v0.9.0 (requirements corpus dropped)
Section titled “Removed - BREAKING - v0.9.0 (requirements corpus dropped)”- The second corpus is gone.
requirements/(PRDs, stories, authoring workflows),mcp/requirement_rules.py, the PRD/STORY templates, both dashboard requirement pages and the nav item, and thevalidate-requirementsCI job are all removed. This reverts the v0.6.0 two-corpus feature; the server serves standards only. - Tool surface shrinks again:
playbook_start(project, intent)-mode=andref=removed. It no longer authors PRDs/stories or bundles a requirement.playbook_get(project, ref)- thereq:ref kind is removed.playbook_find(project, query?, type?)-corpus=,status=andprd=are removed.
- The corpus abstraction goes with it. With one corpus,
corpus=was a parameter that could only ever hold one value, so it is gone fromDocStore(35 call sites),RuleDoc,SearchResult, the BM25 engine, andCorpusSpec- which also loses
cache_policy/ttl_seconds.DocStoredropsfind_by_id,stories_of,prd_ofandreplace_corpus, gainingreplace_all.
- which also loses
MCP_REQUIREMENTS_ROOTandMCP_REQUIREMENTS_TTLare removed, along with the TTL reload poll that ran before every tool dispatch.POST /dashboard/reloadnow reloads standards instead of requirements, so the dashboard’s reload button keeps working - edits tostandards/no longer need a restart.- Metrics keep their history. The
requirement_id/corpuscolumns stay in the schema so pre-0.9.0 rows still read back, and_LEGACY_TOOL_MAPstill folds the old tool names onto the current three. Only the write path and the requirement-coverage KPI are removed. standards/apache-camel/removed;standards/nexre/is the reference project.- Version bumped to 0.9.0.
Changed - BREAKING - v0.8.0 (tool surface → 3, ref doc addresses)
Section titled “Changed - BREAKING - v0.8.0 (tool surface → 3, ref doc addresses)”- Five tools → three.
playbook_start,playbook_get,playbook_find.playbook_start_task+playbook_start_requirementmerge intoplaybook_start(mode="code"|"prd"|"story");playbook_search_docs+playbook_list_requirementsmerge intoplaybook_find(whosestatus=andprd=filters were the only capability unique to the latter, and now apply to search results as well as listings). playbook_get(ref=…)replacesget_doc(kind=, name=, section=, depth=). Arefis the string the corpus already uses insee_also:/targets:frontmatter -guardrails,pattern:repository,language:kotlin/testing,req:ST-101- so a rendered Next Call can be followed verbatim. The ~100-line_format_callswitch that translated between the two vocabularies is gone, and the grammar now lives in one place (mcp/refs.py) shared by the tools andscripts/validate-rules.py.section=folds into the ref (language:java/testing).depth=is removed: a story always arrives with its parent PRD summary and a PRD with its story list, which is whatstart_task(requirement=)already did unconditionally whileget_docdefaulted it off.
- The duplication is gone, not just the tool count.
playbook_startnow composesget.render_ref()for its guardrails and requirement blocks instead of re-rendering them; a test asserts the two are byte-identical so they cannot drift again. - Clean break on tool names in frontmatter.
tool:entries accept onlyplaybook_start,playbook_get,playbook_find; every pre-0.8.0 alias is rejected by the validator rather than silently rendering nothing. Doc-kind aliases (gates:,requirement:,core:) still resolve. The two standards projects, the requirements project, andTEMPLATE.mdare migrated. - Metrics history is preserved.
_LEGACY_TOOL_MAPfolds every historical tool name onto the new three, so the dashboard keeps one row per tool across the rename. - Rule-engine dedupe.
scripts/validate-rules.pyimportsREQUIRED_FILES/REQUIRED_WORKFLOWSfromquality_rulesinstead of redeclaring them, and validatessee_also:through the samerefs.parse_refthe server uses. - Removed dead code:
metrics.args_to_doc_path(no production caller, and a third copy of the ref→path mapping),loader.bootstrap, and theallow_omit_for_cross_lookupbranch no caller ever passed. - Version bumped to 0.8.0.
Changed - BREAKING - v0.7.0 (tool surface → 5, playbook_ namespace)
Section titled “Changed - BREAKING - v0.7.0 (tool surface → 5, playbook_ namespace)”- All tools renamed with a
playbook_prefix so they cannot collide with other MCP servers in a multi-server editor setup:playbook_start_task,playbook_get_doc,playbook_search_docs(wasfind_rules),playbook_list_requirements,playbook_start_requirement. list_projectsremoved (6 → 5 tools). Every project-resolution error already lists the valid projects, so the tool was redundant.get_doc(doc=…)→playbook_get_doc(section=…). A parameter nameddocon a doc-fetching tool was ambiguous; it selects the language sub-doc (standards|testing|anti-patterns).projectis now required on every tool (was optional onstart_task/get_doc). No inference: the agent always states which project’s rules it wants; wrong/missing values still return the teaching error with the valid project list.- Server-level
instructionsnow carry the cross-tool workflow (“call playbook_start_task first…”), so tool descriptions are declarative (when to use + inputs + returns) instead of ALL-CAPS orchestration. - Old tool names remain valid in
see_also:/targets:frontmatter and render as the new names; dashboard metrics queries count old and new names together. Fixed a stalestart_tasktruncation hint that pointed at the removedget_agents_mdtool. - Version bumped to 0.7.0.
Added - v0.6.0 two-corpus (standards + requirements)
Section titled “Added - v0.6.0 two-corpus (standards + requirements)”standards/+requirements/roots. Projects moved understandards/; PRDs/stories live underrequirements/<project>/PRD-*/.- New tools:
list_requirements,start_requirement. Fetching a PRD/story isget_doc(kind="requirement")- the requirements corpus is free once kinds share one tool.start_task(requirement=)does a server-side tree walk. find_rules(corpus=)-standards(default) |requirements|all.- TTL cache for requirements (
MCP_REQUIREMENTS_TTL), corpus env roots,/dashboard/requirements,POST /dashboard/reload. - Status-aware requirement quality rules; CI split validate-standards / validate-requirements.
- Proof PRD:
requirements/nexre/PRD-001-offline-sync/with 3 stories.
Changed - BREAKING (tool surface → 6)
Section titled “Changed - BREAKING (tool surface → 6)”- Nine
get_*tools → oneget_doc(kind=…).get_agents_md,get_guardrails,get_architecture,get_language_rules,get_pattern,get_skill,get_workflow,get_gate, andget_requirementare gone.kindselects the family;name/doc/depthcarry the former per-tool args. Corpus is implied by kind. start_task(project?)- project is optional. Inferred when exactly one standards project exists; otherwise returns a short which-project list so it remains a genuine first call.list_rule_docs+search_rules→find_rules(…). Omitqueryto list docs (includingtriggers:), passqueryfor BM25 search.get_indexremoved. Trigger map is surfaced byfind_ruleslist mode.start_taskis the sole coding entry point.## Next Callsnow rendersget_doc(kind=…)for everysee_also:/targets:entry.- Version unified to 0.6.0 (
pyproject.toml+SERVER_VERSION). - README auth section rewritten for local SQLite + pbkdf2 (was incorrectly documenting Keycloak).
get_agents_mdwas a dead end. It is the only doc tool whose content had nosee_also:frontmatter, so it returned no## Next Callsblock and the agent’s chain stopped there - which is why it dominated the call log. BothAGENTS.mdfiles now declaresee_also: [tool:start_task, …].see_alsoentries with an unrecognized kind were silently dropped._format_callknew only 6 kinds, socore:guardrails(3 nexre workflows) andgates:README(standards/nexre/skills/release.md) rendered nothing. Added thetool:andcore:kinds and thegatesalias, andvalidate-rules.pynow fails on an unknown kind instead of letting it disappear.get_gate(name=…)now returns the script’s first lines, as its description always promised (it previously returned only the path).start_taskno longer collides the workflow body with the trailing---rule.- Default
admin/adminrefused whenMCP_HOST=0.0.0.0. validate-rules.pyerror list typed as_Errortuples.
- All tools are annotated
readOnlyHint: true/openWorldHint: false, stamped centrally inlist_tools()so a new tool cannot omit them.
[0.3.0]
Section titled “[0.3.0]”- Usage dashboard at
/dashboard/(Starlette + Jinja2). Shows users / adoption, tool popularity + latency, search query log + zero-result queries, recent activity feed, and per-user drill-downs. Sameauth.enabledflag gates both/sseand/dashboard. - SQLite-backed metrics: every MCP tool call and every (user, editor) registration is recorded. New tables:
registrations,calls. Database path configurable viaMCP_DB_PATH(defaultmcp/data/metrics.db); excluded from git. - Identity middleware (
identity.py): whenauth.enabled=true, validates Keycloak Bearer tokens and usespreferred_username. Whenauth.enabled=false, identifies callers via advisoryX-MCP-Userheader (or?user=query param), falling back to client IP. - New env vars:
MCP_HOST(default127.0.0.1),MCP_DB_PATH,MCP_INACTIVE_DAYS(default 2 - threshold for “inactive” status). GET /healthzliveness probe.- Editor detection from User-Agent (Claude Code, Cursor, Windsurf, Zed, VS Code).
- New tests:
test_metrics.py,test_identity.py,test_dashboard.py(~50 new tests).
Changed
Section titled “Changed”- Breaking: stdio transport removed. The server is now SSE-only. Each install is a centrally hosted HTTP service that editors connect to.
setup-claude-code.shnow takes an SSE URL argument and registers viaclaude mcp add --transport sse. OptionalBATON_RULES_TOKENenv var addsAuthorization: Bearer ….- MCP server version bumped from
0.2.0to0.3.0. - New runtime dependency:
jinja2(for dashboard templates). Already-requiredstarlette/uvicorn/httpxnow also serve the dashboard. mcp/README.mdand rootREADME.mdrewritten for the hosted-server model.
Removed
Section titled “Removed”mcp_sse_asgi’s oldKeycloakBearerAuthMiddlewarewas extracted and generalized intoidentity.IdentityMiddleware.
- Root
README.mdwith quickstart, repo layout, troubleshooting. CONTRIBUTING.md- author guide, pattern-vs-skill rule, naming conventions.TEMPLATE.md- copy-pasteable templates foragents.md, patterns, and skills.LICENSE(Apache 2.0),.gitignore,.editorconfig.scripts/setup-claude-code.sh- auto-detects absolute paths and registers the MCP server.scripts/validate-rules.py- pre-commit/CI gate for rule docs..pre-commit-config.yamland.github/workflows/ci.yml(lint + validate + test).mcp/tests/- unit tests for loader, search, and server tool handlers.- MCP server:
list_rule_docs(project, doc_type?)tool - agents can now discover patterns/skills without reading them. - MCP server:
get_skill(project, skill)tool - symmetric withget_pattern. - MCP server: optional YAML frontmatter on rule docs (
title,description,tags,applies_to); used to weight BM25 ranking. - MCP server: snippets are now annotated with their parent markdown heading.
- MCP server: every tool invocation is logged at INFO with name + arguments.
- MCP server:
MCP_SNIPPET_SIZEenv var to tune snippet size. baton-sso-config/: stubarchitecture.md,error-conventions.md,anti-patterns.md,glossary.md,skills/dir.
Changed
Section titled “Changed”- MCP server version bumped from
0.1.0to0.2.0. - BM25 index now weights H1/H2/H3 headings and frontmatter
title/tags2× over body text. search_rulesdefaulttop_kraised from 5 to 10; bounds now enforced (1 - 50).- Better startup error message when no rule docs are loaded - lists the directories that were found.
- “Project not found” errors no longer dump the full project list inline; suggest
list_projectsinstead. mcp/pyproject.tomldeclaresuvicorn,starlette,sse-starletteexplicitly (no longer relying onmcp[cli]transitive deps).mcp/README.mdno longer claims “no environment variables” - env vars are now documented in a table.
Removed
Section titled “Removed”- Breaking:
get_error_conventionsMCP tool - useget_rules(project, context="error-conventions")instead. Agents that hardcoded the old name will need to update.
- Loader now warns (instead of silently skipping) when a markdown file lands in
doc_type="other"or sits at the repo root outside any<project>/dir. - Cursor config example removed unnecessary
cwdfield; the server resolves paths from__file__.