create-academic-research 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -104,6 +104,7 @@ npx academic-research mcp enabled
104
104
  npx academic-research mcp available
105
105
  npx academic-research mcp commands arxiv
106
106
  npx academic-research mcp env openalex semantic-scholar zotero
107
+ npx academic-research mcp env --dotenv --all > .env.example
107
108
  npx academic-research mcp enable arxiv dblp
108
109
  npx academic-research mcp disable arxiv
109
110
  npx academic-research mcp install arxiv
@@ -137,7 +138,7 @@ MCP commands are split by side-effect:
137
138
  | `mcp enabled` | List only enabled MCP server ids. |
138
139
  | `mcp available` | List the local MCP catalog. |
139
140
  | `mcp commands` | Print finite external install commands without running them. Runtime-only `uvx`/`npx` servers may have no install command. |
140
- | `mcp env` | Print required/recommended env vars, hosted endpoints, local prerequisites, and setup commands for selected servers. |
141
+ | `mcp env` | Print required/recommended env vars, hosted endpoints, local prerequisites, and setup commands for selected servers. Use `--dotenv --all` to regenerate `.env.example`. |
141
142
  | `mcp enable` | Enable an MCP server in project records and generated snippets. |
142
143
  | `mcp disable` | Remove an MCP server from project records and generated snippets. |
143
144
  | `mcp install` | Run finite external tool install commands for selected MCP servers. It must not launch stdio MCP servers. |
@@ -185,11 +186,17 @@ The MCP catalog distinguishes local runtime adapters from hosted endpoints and
185
186
  manual integrations. arXiv and DBLP are low-friction local `uvx` runtimes.
186
187
  Semantic Scholar is useful for citation graphs but works best with
187
188
  `SEMANTIC_SCHOLAR_API_KEY`. OpenAlex requires `OPENALEX_API_KEY` for the
188
- selected local adapter; OpenAlex keys are free for normal academic use with
189
- daily free usage. PubMed is a biomedical-specific `npx` runtime and remains
190
- opt-in. Zotero needs the local Zotero desktop app and Zoty setup. Overleaf is
191
- manual and credentialed. Crossref and broad paper-search aggregators are kept
192
- as fallback/manual entries until a project explicitly needs them.
189
+ selected local adapter; OpenAlex keys are free and include a free daily quota,
190
+ but high-volume work should check current credit limits. PubMed is a
191
+ biomedical-specific `npx` runtime and remains opt-in. Zotero needs the local
192
+ Zotero desktop app and Zoty setup. Overleaf is manual and credentialed.
193
+ Crossref and broad paper-search aggregators are kept as fallback/manual entries
194
+ until a project explicitly needs them.
195
+
196
+ Generated projects include a committed `.env.example` with empty MCP variables
197
+ and ignore filled `.env` or `.env.local` files. `mcp doctor` checks the current
198
+ process environment or client-provided secrets; it does not load `.env.local`
199
+ automatically.
193
200
 
194
201
  Generated MCP snippets are project documentation and client-ready config, not
195
202
  live tools by themselves. Your MCP client must load the generated snippet, and
@@ -216,8 +223,8 @@ Releases are tag-driven. Update `package.json` and `package-lock.json`, commit
216
223
  the change, create `vX.Y.Z`, and push the tag:
217
224
 
218
225
  ```bash
219
- git tag -a v0.1.8 -m "v0.1.8"
220
- git push origin main v0.1.8
226
+ git tag -a v0.1.9 -m "v0.1.9"
227
+ git push origin main v0.1.9
221
228
  ```
222
229
 
223
230
  Once the GitHub repository is public, the release workflow validates the tag
@@ -30,11 +30,18 @@ export interface McpDoctorResult {
30
30
  warnings: string[];
31
31
  enabled: string[];
32
32
  }
33
+ export interface McpEnvironmentEntry {
34
+ server: string;
35
+ kind: "required" | "recommended" | "default";
36
+ name: string;
37
+ value: string;
38
+ }
33
39
  interface SkillInstallOptions {
34
40
  agent?: string;
35
41
  }
36
42
  export declare function readCapabilities(root: string): Promise<CapabilityState>;
37
43
  export declare function writeCapabilities(root: string, state: Partial<CapabilityState>): Promise<void>;
44
+ export declare function writeMcpEnvironmentExample(root: string): Promise<void>;
38
45
  export declare function initializeCapabilities(root: string, options?: InitializeCapabilitiesOptions): Promise<void>;
39
46
  export declare function buildSkillInstallCommands(root: string, preset?: string, options?: SkillInstallOptions): Promise<string[][]>;
40
47
  export declare function buildExplicitSkillInstallCommands(root: string, skills: string[], options?: SkillInstallOptions): Promise<string[][]>;
@@ -51,6 +58,14 @@ export declare function disableMcpServers(root: string, servers: string[], optio
51
58
  }): Promise<CapabilityCommandResult>;
52
59
  export declare function mcpToolCommands(servers: string[], key?: McpToolCommandKey): string[][];
53
60
  export declare function mcpToolCommandTexts(servers: string[], key?: McpToolCommandKey): string[];
61
+ export declare function listMcpEnvironmentEntries(servers: string[], options?: {
62
+ requiredOnly?: boolean;
63
+ recommendedOnly?: boolean;
64
+ }): McpEnvironmentEntry[];
65
+ export declare function formatMcpDotenv(servers: string[], options?: {
66
+ requiredOnly?: boolean;
67
+ recommendedOnly?: boolean;
68
+ }): string;
54
69
  export declare function installMcpTools(root: string, servers: string[], runner?: Runner): Promise<CapabilityCommandResult>;
55
70
  export declare function uninstallMcpTools(root: string, servers: string[], runner?: Runner): Promise<CapabilityCommandResult>;
56
71
  export declare function doctorMcpServers(root: string): Promise<McpDoctorResult>;
@@ -29,6 +29,9 @@ export async function writeCapabilities(root, state) {
29
29
  await writeMcpSnippet(root, next);
30
30
  await appendCapabilityLog(root, next);
31
31
  }
32
+ export async function writeMcpEnvironmentExample(root) {
33
+ await writeFile(join(root, ".env.example"), formatMcpDotenv(Object.keys(AGENT_STACK.mcp_servers)), "utf8");
34
+ }
32
35
  export async function initializeCapabilities(root, options = {}) {
33
36
  const preset = options.preset ?? "default";
34
37
  const mcpServers = options.mcpServers ?? presetMcpServers(preset);
@@ -204,6 +207,50 @@ export function mcpToolCommandTexts(servers, key = "install_command") {
204
207
  }
205
208
  return commands;
206
209
  }
210
+ export function listMcpEnvironmentEntries(servers, options = {}) {
211
+ assertKnownMcpServers(servers);
212
+ const entries = [];
213
+ for (const serverName of servers) {
214
+ const server = AGENT_STACK.mcp_servers[serverName];
215
+ if (!options.recommendedOnly) {
216
+ for (const envName of server.required_env) {
217
+ entries.push({ server: serverName, kind: "required", name: envName, value: "" });
218
+ }
219
+ }
220
+ if (!options.requiredOnly) {
221
+ for (const envName of server.recommended_env) {
222
+ entries.push({ server: serverName, kind: "recommended", name: envName, value: "" });
223
+ }
224
+ for (const [envName, value] of Object.entries(server.env)) {
225
+ entries.push({ server: serverName, kind: "default", name: envName, value });
226
+ }
227
+ }
228
+ }
229
+ return dedupeMcpEnvironmentEntries(entries);
230
+ }
231
+ export function formatMcpDotenv(servers, options = {}) {
232
+ const entries = listMcpEnvironmentEntries(servers, options);
233
+ const lines = [
234
+ "# Academic research MCP environment example.",
235
+ "# Copy to .env.local, your shell profile, or your MCP client secret store.",
236
+ "# Do not commit filled secrets. Empty values mean optional or user-supplied.",
237
+ ""
238
+ ];
239
+ let previousServer = "";
240
+ for (const entry of entries) {
241
+ if (entry.server !== previousServer) {
242
+ if (previousServer)
243
+ lines.push("");
244
+ lines.push(`# ${entry.server} environment`);
245
+ previousServer = entry.server;
246
+ }
247
+ lines.push(`${entry.name}=${dotenvValue(entry.value)}`);
248
+ }
249
+ if (entries.length === 0) {
250
+ lines.push("# No environment variables are required for the selected MCP servers.");
251
+ }
252
+ return `${lines.join("\n")}\n`;
253
+ }
207
254
  export async function installMcpTools(root, servers, runner = defaultRunner) {
208
255
  const selected = servers.length > 0 ? servers : (await readCapabilities(root)).mcp_servers ?? [];
209
256
  assertKnownMcpServers(selected);
@@ -369,7 +416,7 @@ async function writeMcpSetup(root, state) {
369
416
  lines.push(`- \`${name}\` (${status}, ${server.readiness}, ${server.priority}): ${server.source_need}`, ` - Source: \`${server.source}\``, ` - Execution mode: \`${server.execution_mode}\``, ...(server.hosted_url ? [` - Hosted endpoint: <${server.hosted_url}>`] : []), ...server.setup_commands.map((command) => ` - Setup command: \`${command}\``));
370
417
  appendMcpPrerequisiteLines(lines, server.required_env, server.recommended_env, server.local_service);
371
418
  }
372
- lines.push("", "## Operating Rules", "", "- Keep secrets in your shell, MCP client secret store, or local untracked files; do not commit tokens or API keys.", "- Prefer the smallest enabled MCP set that covers the current research question.", "- Treat MCP output as retrieval metadata. Promote claims into repository source records only after source ingestion and citation audit.", "- Run `npx academic-research mcp doctor` after changing MCP records or environment variables.", "");
419
+ lines.push("", "## Operating Rules", "", "- Use `.env.example` as a committed reference and put filled secrets in `.env.local`, your shell, or your MCP client secret store.", "- Regenerate a dotenv-style reference with `npx academic-research mcp env --dotenv --all`.", "- Keep secrets in your shell, MCP client secret store, or local untracked files; do not commit tokens or API keys.", "- Prefer the smallest enabled MCP set that covers the current research question.", "- Treat MCP output as retrieval metadata. Promote claims into repository source records only after source ingestion and citation audit.", "- Run `npx academic-research mcp doctor` after changing MCP records or environment variables.", "");
373
420
  await mkdir(join(root, "docs/agent"), { recursive: true });
374
421
  await writeFile(join(root, "docs/agent/mcp-setup.md"), lines.join("\n"), "utf8");
375
422
  }
@@ -382,6 +429,24 @@ async function appendCapabilityLog(root, state) {
382
429
  function dedupe(values) {
383
430
  return [...new Set(values)];
384
431
  }
432
+ function dedupeMcpEnvironmentEntries(entries) {
433
+ const priority = { required: 0, default: 1, recommended: 2 };
434
+ const byName = new Map();
435
+ for (const entry of entries) {
436
+ const previous = byName.get(entry.name);
437
+ if (!previous || priority[entry.kind] < priority[previous.kind]) {
438
+ byName.set(entry.name, entry);
439
+ }
440
+ }
441
+ return [...byName.values()];
442
+ }
443
+ function dotenvValue(value) {
444
+ if (!value)
445
+ return "";
446
+ if (/^[A-Za-z0-9_./:-]+$/.test(value))
447
+ return value;
448
+ return JSON.stringify(value);
449
+ }
385
450
  function renderSkillCommand(command, agent) {
386
451
  const normalized = assertKnownAgentTarget(agent);
387
452
  const agentFlag = normalized === AUTO_AGENT ? "" : `--agent '${normalized}'`;
package/dist/src/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { basename, delimiter, dirname, join, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- import { assertKnownMcpServers, disableMcpServers, doctorMcpServers, enableMcpServers, DEFAULT_AGENT, installMcpTools, installSkillIds, installSkills, listInstalledSkills, mcpToolCommandTexts, readCapabilities, removeSkills, uninstallMcpTools, updateSkills } from "./capabilities.js";
4
+ import { assertKnownMcpServers, disableMcpServers, doctorMcpServers, enableMcpServers, DEFAULT_AGENT, installMcpTools, installSkillIds, installSkills, formatMcpDotenv, listInstalledSkills, listMcpEnvironmentEntries, mcpToolCommandTexts, readCapabilities, removeSkills, uninstallMcpTools, updateSkills } from "./capabilities.js";
5
5
  import { createProject, doctorProject, renameProject } from "./project.js";
6
6
  import { askCreateOptions } from "./prompts.js";
7
7
  import { AGENT_STACK, presetMcpServers } from "./stack.js";
@@ -21,7 +21,7 @@ const CREATE_FLAGS = flagSchema([
21
21
  const ROOT_FLAGS = flagSchema(["help"], ["root"]);
22
22
  const RENAME_FLAGS = flagSchema(["help"], ["root", "title", "slug", "package"]);
23
23
  const SKILLS_FLAGS = flagSchema(["help"], ["root", "preset", "agent"]);
24
- const MCP_FLAGS = flagSchema(["help"], ["root", "agent"]);
24
+ const MCP_FLAGS = flagSchema(["help", "all", "dotenv", "required", "recommended"], ["root", "agent"]);
25
25
  export async function main(argv = process.argv.slice(2), mode = "create") {
26
26
  try {
27
27
  if (mode === "create")
@@ -171,6 +171,7 @@ async function setupCommand(argv) {
171
171
  console.log("academic-research skills status");
172
172
  console.log("academic-research mcp list");
173
173
  console.log("academic-research mcp env");
174
+ console.log("academic-research mcp env --dotenv --all");
174
175
  console.log("academic-research mcp smoke");
175
176
  console.log("academic-research doctor");
176
177
  return project.ok ? 0 : 1;
@@ -339,12 +340,27 @@ async function mcpCommand(argv) {
339
340
  return 0;
340
341
  }
341
342
  if (subcommand === "env") {
342
- assertOnlyOptions(parsed.flags, "mcp env", ["root"]);
343
+ assertOnlyOptions(parsed.flags, "mcp env", ["root", "all", "dotenv", "required", "recommended"]);
343
344
  const root = resolve(flagString(parsed.flags, "root") ?? ".");
344
- const selected = parsed.positionals.length > 0 ? parsed.positionals : (await readCapabilities(root)).mcp_servers;
345
+ if (flagBool(parsed.flags, "required") && flagBool(parsed.flags, "recommended")) {
346
+ throw new Error("mcp env cannot use --required and --recommended together");
347
+ }
348
+ const selected = flagBool(parsed.flags, "all")
349
+ ? Object.keys(AGENT_STACK.mcp_servers)
350
+ : parsed.positionals.length > 0
351
+ ? parsed.positionals
352
+ : (await readCapabilities(root)).mcp_servers;
345
353
  assertKnownMcpServers(selected);
354
+ const filters = {
355
+ requiredOnly: flagBool(parsed.flags, "required"),
356
+ recommendedOnly: flagBool(parsed.flags, "recommended")
357
+ };
358
+ if (flagBool(parsed.flags, "dotenv")) {
359
+ process.stdout.write(formatMcpDotenv(selected, filters));
360
+ return 0;
361
+ }
346
362
  console.log("id\ttype\tvalue");
347
- printMcpEnvironment(selected);
363
+ printMcpEnvironment(selected, filters);
348
364
  return 0;
349
365
  }
350
366
  if (subcommand === "enable") {
@@ -519,6 +535,7 @@ export function formatInteractiveCreateGuide() {
519
535
  " MCP installers are optional and run only finite installer commands.",
520
536
  " MCP execution modes are explicit: uvx-runtime, npx-runtime, local-service, manual, or fallback.",
521
537
  " Use `academic-research mcp env <server>` to inspect env vars and local prerequisites.",
538
+ " Use `academic-research mcp env --dotenv --all` to regenerate a committed env example.",
522
539
  ""
523
540
  ].join("\n");
524
541
  }
@@ -613,9 +630,18 @@ function printMcpHelp() {
613
630
  "",
614
631
  "Manage MCP records, readiness checks, and finite external MCP tool installs.",
615
632
  "",
633
+ "Examples:",
634
+ " academic-research mcp env openalex semantic-scholar",
635
+ " academic-research mcp env --dotenv --all > .env.example",
636
+ " academic-research mcp smoke",
637
+ "",
616
638
  "Options:",
617
639
  " --root <path> Project root for project-state commands.",
618
640
  " --agent <id> Agent for enable/disable generated snippets.",
641
+ " --all Select all catalog MCP servers for mcp env.",
642
+ " --dotenv Print mcp env as dotenv content.",
643
+ " --required Print only required env vars for mcp env.",
644
+ " --recommended Print only recommended/default env vars for mcp env.",
619
645
  " -h, --help Show this help."
620
646
  ].join("\n"));
621
647
  }
@@ -645,29 +671,34 @@ function printMcpSmokeDiagnostics(servers) {
645
671
  }
646
672
  return failed;
647
673
  }
648
- function printMcpEnvironment(servers) {
674
+ function printMcpEnvironment(servers, options = {}) {
675
+ const grouped = new Map();
676
+ for (const entry of listMcpEnvironmentEntries(servers, options)) {
677
+ const entries = grouped.get(entry.server) ?? [];
678
+ entries.push(entry);
679
+ grouped.set(entry.server, entries);
680
+ }
649
681
  for (const name of servers) {
650
682
  const server = AGENT_STACK.mcp_servers[name];
683
+ const entries = grouped.get(name) ?? [];
651
684
  let wroteLine = false;
652
- for (const envName of server.required_env) {
653
- console.log(`${name}\trequired\t${envName}`);
685
+ for (const entry of entries) {
686
+ console.log(`${name}\t${entry.kind}\t${entry.name}${entry.value ? `=${entry.value}` : ""}`);
654
687
  wroteLine = true;
655
688
  }
656
- for (const envName of server.recommended_env) {
657
- console.log(`${name}\trecommended\t${envName}`);
658
- wroteLine = true;
659
- }
660
- if (server.hosted_url) {
689
+ if (!options.requiredOnly && !options.recommendedOnly && server.hosted_url) {
661
690
  console.log(`${name}\thosted-endpoint\t${server.hosted_url}`);
662
691
  wroteLine = true;
663
692
  }
664
- if (server.local_service) {
693
+ if (!options.requiredOnly && !options.recommendedOnly && server.local_service) {
665
694
  console.log(`${name}\tlocal-service\t${server.local_service}`);
666
695
  wroteLine = true;
667
696
  }
668
- for (const command of server.setup_commands) {
669
- console.log(`${name}\tsetup-command\t${command}`);
670
- wroteLine = true;
697
+ if (!options.requiredOnly && !options.recommendedOnly) {
698
+ for (const command of server.setup_commands) {
699
+ console.log(`${name}\tsetup-command\t${command}`);
700
+ wroteLine = true;
701
+ }
671
702
  }
672
703
  if (!wroteLine)
673
704
  console.log(`${name}\tnone\t-`);
@@ -2,7 +2,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { basename, dirname, join, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import YAML from "yaml";
5
- import { DEFAULT_AGENT, initializeCapabilities, installSkills } from "./capabilities.js";
5
+ import { DEFAULT_AGENT, initializeCapabilities, installSkills, writeMcpEnvironmentExample } from "./capabilities.js";
6
6
  import { assertKnownAgentTarget } from "./agents.js";
7
7
  import { copyDirectory, exists, isNonEmptyDirectory, movePath, readJson, writeJson } from "./files.js";
8
8
  import { packageify, slugify, titleFromSlug } from "./names.js";
@@ -100,6 +100,7 @@ export async function createProject(options) {
100
100
  await personalizeProject(target, { title, slug, packageName, profile: options.profile ?? "academic-general" });
101
101
  await writeGeneratedPackageJson(target, { slug });
102
102
  await writeAgentStack(target);
103
+ await writeMcpEnvironmentExample(target);
103
104
  await initializeCapabilities(target, { preset, agent });
104
105
  if (options.installSkills) {
105
106
  await installSkills(target, preset);
@@ -129,6 +130,7 @@ export async function doctorProject(root) {
129
130
  const errors = [];
130
131
  const required = [
131
132
  "README.md",
133
+ ".env.example",
132
134
  "package.json",
133
135
  "pyproject.toml",
134
136
  "AGENTS.md",
@@ -137,6 +139,7 @@ export async function doctorProject(root) {
137
139
  "configs/capabilities.yaml",
138
140
  "docs/agent/capability-profile.md",
139
141
  "docs/agent/mcp-setup.md",
142
+ "docs/agent/mcp-client-setup.md",
140
143
  "docs/agent/generated",
141
144
  "scripts/README.md",
142
145
  "sources/source-ledger.csv",
@@ -214,7 +217,7 @@ async function writeGeneratedPackageJson(root, { slug }) {
214
217
  const path = join(root, "package.json");
215
218
  const data = await readJson(path);
216
219
  const existingSpec = data.devDependencies?.["create-academic-research"];
217
- const packageSpec = process.env.CREATE_ACADEMIC_RESEARCH_PACKAGE_SPEC ?? existingSpec ?? "0.1.8";
220
+ const packageSpec = process.env.CREATE_ACADEMIC_RESEARCH_PACKAGE_SPEC ?? existingSpec ?? "0.1.9";
218
221
  data.name = slug;
219
222
  data.devDependencies = {
220
223
  ...(data.devDependencies ?? {}),
package/dist/src/stack.js CHANGED
@@ -180,7 +180,7 @@ export const AGENT_STACK = {
180
180
  recommended_env: [],
181
181
  local_service: "",
182
182
  smoke_test: "Search works by title or DOI and confirm stable OpenAlex IDs.",
183
- risks: "The selected local server requires OPENALEX_API_KEY. OpenAlex keys are free for normal academic use with daily free usage; smoke-test coverage and cost headers before high-volume work."
183
+ risks: "The selected local server requires OPENALEX_API_KEY. OpenAlex keys are free and include a free daily quota; check current credit limits, smoke-test coverage, and inspect cost headers before high-volume work."
184
184
  },
185
185
  crossref: {
186
186
  readiness: "manual",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-academic-research",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Create and manage agent-ready academic research repositories.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,23 @@
1
+ # Academic research MCP environment example.
2
+ # Copy to .env.local, your shell profile, or your MCP client secret store.
3
+ # Do not commit filled secrets. Empty values mean optional or user-supplied.
4
+
5
+ # semantic-scholar environment
6
+ SEMANTIC_SCHOLAR_API_KEY=
7
+
8
+ # openalex environment
9
+ OPENALEX_API_KEY=
10
+
11
+ # pubmed environment
12
+ NCBI_API_KEY=
13
+ NCBI_ADMIN_EMAIL=
14
+ MCP_TRANSPORT_TYPE=stdio
15
+ MCP_LOG_LEVEL=warning
16
+
17
+ # overleaf environment
18
+ OVERLEAF_TOKEN=
19
+ PROJECT_ID=
20
+
21
+ # paper-search environment
22
+ PAPER_SEARCH_MCP_UNPAYWALL_EMAIL=
23
+ PAPER_SEARCH_MCP_SEMANTIC_SCHOLAR_API_KEY=
@@ -11,6 +11,8 @@ scholarly record.
11
11
  - Tie claims to sources, datasets, experiment records, or decision records.
12
12
  - Update durable records when project knowledge changes.
13
13
  - Keep large data, generated caches, credentials, and private review material out of git.
14
+ - Keep `.env.example` as a public reference only; never commit filled `.env`,
15
+ `.env.local`, API keys, tokens, cookies, or browser sessions.
14
16
 
15
17
  ## First Read
16
18
 
@@ -63,6 +63,7 @@ npx academic-research skills status
63
63
  npx academic-research setup
64
64
  npx academic-research mcp list
65
65
  npx academic-research mcp env openalex semantic-scholar zotero
66
+ npx academic-research mcp env --dotenv --all > .env.example
66
67
  npx academic-research mcp enable arxiv dblp
67
68
  npx academic-research mcp commands arxiv
68
69
  npx academic-research mcp install arxiv
@@ -78,6 +79,11 @@ enable optional servers. `mcp install` runs only finite tool installation
78
79
  commands; runtime-only `uvx`/`npx` MCP servers may have no install step and are
79
80
  started later by the MCP client.
80
81
 
82
+ `.env.example` is the committed MCP environment reference. Copy it to
83
+ `.env.local`, your shell profile, or your MCP client secret store when secrets
84
+ are needed. Filled `.env` files are ignored by git. `mcp doctor` checks the
85
+ current process environment; it does not automatically load `.env.local`.
86
+
81
87
  `setup` prints the current project capability state, installed skill counts,
82
88
  enabled MCP records, and the next onboarding commands without changing files.
83
89
  `mcp smoke` performs a non-launching MCP readiness check: it reports required
@@ -0,0 +1,43 @@
1
+ # MCP Client Setup
2
+
3
+ Generated MCP snippets live in `docs/agent/generated/`. They are client-ready
4
+ configuration fragments, not live tools by themselves. The active MCP client
5
+ must load the generated snippet and must receive any required environment
6
+ variables from the shell, a local untracked env file, or the client's secret
7
+ store.
8
+
9
+ ## Files
10
+
11
+ - `docs/agent/generated/mcp.json`: generic/default generated snippet.
12
+ - `docs/agent/generated/<agent>-mcp.json`: generated when a specific agent is
13
+ selected.
14
+ - `.env.example`: committed reference for MCP environment variables.
15
+ - `.env.local`: recommended local untracked file for filled secrets.
16
+
17
+ ## Environment
18
+
19
+ Regenerate the committed reference from the current MCP catalog with:
20
+
21
+ ```bash
22
+ npx academic-research mcp env --dotenv --all > .env.example
23
+ ```
24
+
25
+ Create a private local file when needed:
26
+
27
+ ```bash
28
+ cp .env.example .env.local
29
+ ```
30
+
31
+ Do not commit filled `.env`, `.env.local`, tokens, cookies, or browser sessions.
32
+ `mcp doctor` checks the current process environment; it does not automatically
33
+ load `.env.local`.
34
+
35
+ ## Workflow
36
+
37
+ 1. Enable only the MCP servers needed for the current research task.
38
+ 2. Inspect prerequisites with `npx academic-research mcp env <server>`.
39
+ 3. Put required secrets in the MCP client secret store, shell, or `.env.local`.
40
+ 4. Run `npx academic-research mcp smoke` before wiring the client.
41
+ 5. Load the generated snippet in the MCP client.
42
+ 6. Treat MCP output as retrieval metadata until it is ingested into repository
43
+ source records.
@@ -2,3 +2,7 @@
2
2
 
3
3
  Record active MCP servers, install commands, auth requirements, smoke tests,
4
4
  and known risks here.
5
+
6
+ Use `.env.example` as the committed environment reference. Put filled values in
7
+ `.env.local`, the shell, or the MCP client secret store. Regenerate the example
8
+ with `npx academic-research mcp env --dotenv --all > .env.example`.
@@ -11,10 +11,11 @@
11
11
  "mcp:list": "academic-research mcp list",
12
12
  "mcp:commands": "academic-research mcp commands",
13
13
  "mcp:env": "academic-research mcp env",
14
+ "mcp:dotenv": "academic-research mcp env --dotenv --all",
14
15
  "mcp:smoke": "academic-research mcp smoke",
15
16
  "mcp:doctor": "academic-research mcp doctor"
16
17
  },
17
18
  "devDependencies": {
18
- "create-academic-research": "0.1.8"
19
+ "create-academic-research": "0.1.9"
19
20
  }
20
21
  }