Skip to content

Commit 728c0dd

Browse files
mrdailey99claude
andcommitted
fix(mcp): address PR #114 review comments and add CLAUDE.md
PR comment fixes: - details: pass undefined (not {}) when no suggestion, keeping response shape backward-compatible (Copilot comment on automationTools.ts:271) - flags description: soften "required" wording to avoid misleading callers since connections flag is not enforced server-side (automationTools.ts:257) - testprojectSecrets: change "Leave empty" to "Omit this field" to prevent accidental key removal via empty string (propertiesTools.ts:229) Add CLAUDE.md with project-wide Claude Code guidelines: - Documentation update checklist for every MCP tool change (docs/mcp.md, docs/mcp-pilot-guide.md, README.md, external docs) - Test coverage requirements (unit, smoke, compile gates) - MCP tool authoring standards (description quality, field descriptions, error responses, path safety) - Branch/PR conventions and version bump rules - ESLint common gotchas Also removes CLAUDE.md from .gitignore (project-level instructions should be tracked; .claude/ dir is still ignored for session files). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 53f7fdc commit 728c0dd

4 files changed

Lines changed: 100 additions & 5 deletions

File tree

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ mochawesome-report
5656
.env.*.local
5757

5858
# Claude
59-
.claude/skills
60-
CLAUDE.md
59+
.claude/
6160

6261
# NitroX schema files — do not commit until IP/licensing confirmed with Provar team
6362
# See: src/mcp/tools/nitroXTools.ts and plan notes

CLAUDE.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Claude Code — Project Instructions
2+
3+
This file is read automatically by Claude Code at the start of every session. Follow these rules when working in this repo.
4+
5+
---
6+
7+
## Documentation Requirements
8+
9+
**Every PR that adds, modifies, or removes an MCP tool must update documentation.** The full set of places to update:
10+
11+
| What changed | Where to update |
12+
|---|---|
13+
| New MCP tool | `docs/mcp.md` (add tool entry with schema + example), `docs/mcp-pilot-guide.md` (add evaluation scenario if user-facing), `README.md` (update tool count if referenced) |
14+
| Modified tool description / parameters / errors | `docs/mcp.md` (update the relevant tool section) |
15+
| Removed tool | `docs/mcp.md`, `docs/mcp-pilot-guide.md`, `README.md` |
16+
| New error code or suggestion field | `docs/mcp.md` (Troubleshooting or Error Codes section) |
17+
| Security model change (path policy, licence, transport) | `docs/mcp.md` (Security Model section), `docs/mcp-pilot-guide.md` (Security Model section) |
18+
| New NitroX tool or schema rule | `docs/mcp.md` (NitroX section), `docs/mcp-pilot-guide.md` (Scenario 7) |
19+
| Smoke test count change | Update `TOTAL_EXPECTED` in `scripts/mcp-smoke.cjs` |
20+
21+
**External / customer-facing docs** (`docs/provar-mcp-public-docs.md`, `docs/university-of-provar-mcp-course.md`) are maintained separately by the Provar team — flag changes that affect public-facing behaviour in your PR description so those can be updated manually.
22+
23+
---
24+
25+
## Test Coverage Requirements
26+
27+
Every PR must include tests for new or changed behaviour:
28+
29+
- **New MCP tool** → unit tests in `test/unit/mcp/<toolGroup>Tools.test.ts` covering: happy path, path policy rejection, missing required fields, and any tool-specific error codes
30+
- **Modified error handling** → at least one positive test (error path fires) and one negative test (does not fire for non-matching input)
31+
- **New validation rule** → test that the rule fires correctly and that a valid input passes
32+
- **Smoke test** → add an entry in `scripts/mcp-smoke.cjs` for each new tool; update `TOTAL_EXPECTED`
33+
34+
Run before every commit:
35+
```sh
36+
yarn test:only # unit tests — must all pass
37+
node scripts/mcp-smoke.cjs 2>/dev/null # smoke — must show all PASS
38+
yarn compile # TypeScript — must be clean
39+
```
40+
41+
---
42+
43+
## MCP Tool Authoring Standards
44+
45+
### Tool description quality
46+
47+
Every tool description must answer these questions for an AI agent reading it cold:
48+
49+
1. **What does it do?** (one sentence)
50+
2. **What prerequisite tools must run first?** (e.g. `config.load` before `metadata.download`)
51+
3. **What are the correct flags / parameters?** Include a concrete example in the `flags` field description when flags are free-form
52+
4. **What does a failure mean?** If a known error pattern exists (e.g. `[DOWNLOAD_ERROR]` = auth failure), say so in the description or return a `details.suggestion`
53+
54+
### Field descriptions
55+
56+
- Fields that accept a **string key or password** must say "string value, NOT a file path" if there is any risk of confusion with a path
57+
- Fields that accept a **file path** must note if the path must be within `--allowed-paths`
58+
- Optional fields that have a dangerous default (e.g. overwriting existing files) must call that out
59+
60+
### Error responses
61+
62+
- Return `details: { suggestion: '...' }` when a known error pattern maps to a common root cause and there is an actionable fix
63+
- Never pass `details: {}` — omit `details` entirely when there is nothing extra to say (keeps the response shape stable)
64+
- Error codes follow `SCREAMING_SNAKE_CASE`; document new codes in `docs/mcp.md`
65+
66+
### Path safety
67+
68+
- Call `assertPathAllowed(path, config.allowedPaths)` on **every** path input before any file operation — not just the output path
69+
- Use `path.resolve()` before `fs.existsSync` / `fs.readFileSync` / `fs.writeFileSync`
70+
- Never construct shell commands from user input; use `spawnSync` with an args array
71+
72+
---
73+
74+
## Branch and PR Conventions
75+
76+
- Feature branches off `develop`: `feature/<description>`
77+
- Bug/fix branches off `develop`: `fix/<description>`
78+
- Release branches off `develop`: `release-v<semver>`
79+
- PRs target `develop`; releases are merged develop → main
80+
- Version in `package.json` follows `<major>.<minor>.<patch>-beta.<n>` on develop
81+
- Bump the beta suffix (`beta.N → beta.N+1`) on any PR that triggers a publish
82+
83+
---
84+
85+
## Lint
86+
87+
The project uses ESLint with `@typescript-eslint` strict rules. Common gotchas:
88+
89+
- `complexity` max is **20** — extract helpers if a function grows past that
90+
- `no-unsafe-assignment` / `no-unsafe-call` — cast through `unknown` not `any`
91+
- `no-unnecessary-type-assertion` — TypeScript narrows after `typeof x === 'string'` checks; remove the redundant cast
92+
- `camelcase``nitroX` is valid camelCase (capital X starts the next word)
93+
94+
CI runs lint as part of `sf-prepack` — do not skip with `--no-verify` on the final merge commit.

src/mcp/tools/automationTools.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ export function registerAutomationMetadataDownload(server: McpServer): void {
254254
].join(' '),
255255
{
256256
flags: z.array(z.string()).optional().default([]).describe(
257-
'Raw CLI flags to forward. Use ["-c", "Name1,Name2"] to specify connections (required). Example: ["-c", "MyOrg,SandboxOrg"]'
257+
'Raw CLI flags to forward. Use ["-c", "Name1,Name2"] (or the equivalent --connections form) to target specific connections. Example: ["-c", "MyOrg,SandboxOrg"]'
258258
),
259259
sf_path: z.string().optional().describe('Path to the sf CLI executable when not in PATH (e.g. "~/.nvm/versions/node/v22.0.0/bin/sf")'),
260260
},
@@ -268,7 +268,9 @@ export function registerAutomationMetadataDownload(server: McpServer): void {
268268

269269
if (result.exitCode !== 0) {
270270
const isDownloadError = message.includes('[DOWNLOAD_ERROR]');
271-
const details: Record<string, unknown> = isDownloadError ? { suggestion: DOWNLOAD_ERROR_SUGGESTION } : {};
271+
const details: Record<string, unknown> | undefined = isDownloadError
272+
? { suggestion: DOWNLOAD_ERROR_SUGGESTION }
273+
: undefined;
272274
return { isError: true as const, content: [{ type: 'text' as const, text: JSON.stringify(makeError('AUTOMATION_METADATA_FAILED', message, requestId, false, details)) }] };
273275
}
274276

src/mcp/tools/propertiesTools.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ const updatesSchema = z.object({
226226
excludeCallable: z.boolean().optional().describe('Omit callable test cases from execution'),
227227
testprojectSecrets: z.string().optional().describe(
228228
'Encryption key (password string) used to decrypt the .secrets file in the Provar project root. ' +
229-
'This is the key itself — NOT a file path. Leave empty if your project does not use secrets encryption.'
229+
'This is the key itself — NOT a file path. Omit this field unless your project uses secrets encryption.'
230230
),
231231
environment: z.object({
232232
testEnvironment: z.string().optional().describe('Name of the test environment to run against'),

0 commit comments

Comments
 (0)