create-academic-research 0.1.4 → 0.1.6

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
@@ -35,9 +35,10 @@ adjacent interdisciplinary CS.
35
35
 
36
36
  The generated repository is agent-neutral. By default the wizard records
37
37
  `agent: universal`, installs one shared project-local `.agents/skills` copy,
38
- and writes generic MCP snippets. Use `--agent <name>` only when you want to
39
- force a specific target recognized by the `skills` CLI, such as `claude-code`,
40
- `codex`, `cursor`, `windsurf`, or another supported local loader.
38
+ and writes generic MCP snippets. Use `--agent <id>` only when you want to force
39
+ a specific target recognized by the `skills` CLI. Run
40
+ `npx academic-research agents list` inside a generated project to see every
41
+ supported target and alias.
41
42
 
42
43
  ## Default Experience
43
44
 
@@ -45,10 +46,12 @@ By default, the wizard:
45
46
 
46
47
  - creates the repository structure;
47
48
  - installs the project-local `VincenzoImp/academic-research-skills` package;
48
- - enables the scholarly MCP records for `arxiv`, `semantic-scholar`, and
49
- `openalex`;
49
+ - enables only the low-friction `arxiv` MCP record;
50
+ - documents the wider MCP catalog, including Semantic Scholar, OpenAlex, DBLP,
51
+ PubMed, Zotero, Crossref, Overleaf, and fallback aggregators;
50
52
  - writes `configs/capabilities.yaml`;
51
53
  - writes `docs/agent/capability-profile.md`;
54
+ - writes `docs/agent/mcp-setup.md`;
52
55
  - writes `docs/agent/generated/mcp.json` unless an explicit agent target is set;
53
56
  - appends the onboarding event to `wiki/log.md`;
54
57
  - does not install external MCP tools unless explicitly requested.
@@ -57,6 +60,12 @@ Use `--preset enhanced` when you also want the curated complementary external
57
60
  skill bundles for agent engineering, frontend work, testing, document formats,
58
61
  and PDF conversion.
59
62
 
63
+ Research-specific skills are intentionally not pulled from external packages by
64
+ default. The academic research workflow, literature review, citation audit,
65
+ paper writing, peer review, rebuttal, and reproduction policies come from
66
+ `VincenzoImp/academic-research-skills`. External skills installed by the wizard
67
+ are complementary tooling only.
68
+
60
69
  ## Non-Interactive Create
61
70
 
62
71
  ```bash
@@ -76,6 +85,7 @@ Inside a generated project:
76
85
  ```bash
77
86
  npx academic-research doctor
78
87
  npx academic-research rename --title "New Title" --slug new-title --package new_title
88
+ npx academic-research agents list
79
89
  npx academic-research skills presets
80
90
  npx academic-research skills install --preset default
81
91
  npx academic-research skills install --preset enhanced
@@ -88,6 +98,7 @@ npx academic-research mcp list
88
98
  npx academic-research mcp enabled
89
99
  npx academic-research mcp available
90
100
  npx academic-research mcp commands arxiv
101
+ npx academic-research mcp env openalex semantic-scholar zotero
91
102
  npx academic-research mcp enable arxiv openalex
92
103
  npx academic-research mcp disable arxiv
93
104
  npx academic-research mcp install arxiv
@@ -116,11 +127,12 @@ MCP commands are split by side-effect:
116
127
  | `mcp enabled` | List only enabled MCP server ids. |
117
128
  | `mcp available` | List the local MCP catalog. |
118
129
  | `mcp commands` | Print finite external install commands without running them. Runtime-only `uvx`/`npx` servers may have no install command. |
130
+ | `mcp env` | Print required/recommended env vars, hosted endpoints, local prerequisites, and setup commands for selected servers. |
119
131
  | `mcp enable` | Enable an MCP server in project records and generated snippets. |
120
132
  | `mcp disable` | Remove an MCP server from project records and generated snippets. |
121
133
  | `mcp install` | Run finite external tool install commands for selected MCP servers. It must not launch stdio MCP servers. |
122
134
  | `mcp uninstall` | Run the external uninstall command when one exists. |
123
- | `mcp doctor` | Validate enabled MCP records and generated snippets. |
135
+ | `mcp doctor` | Validate enabled MCP records, generated snippets, required env vars, and documented manual prerequisites. |
124
136
 
125
137
  ## Companion Skills
126
138
 
@@ -134,8 +146,9 @@ The create wizard can install that project-local package automatically.
134
146
  Those skills are portable `SKILL.md` instructions, but they require an
135
147
  agent/runtime that can load skills or include the relevant instructions in
136
148
  context. They are not automatic capabilities of every raw model API.
137
- Use `--agent <agent>` for explicit setup, for example `--agent codex` or
138
- `--agent claude-code`.
149
+ Use `--agent <id>` for explicit setup with any id from
150
+ `academic-research agents list`. The shorthand `--agent claude` is normalized
151
+ to the supported `claude-code` target.
139
152
  Avoid `--agent auto` for unattended setup: the upstream `skills` CLI may expand
140
153
  it to every agent it detects on the machine.
141
154
 
@@ -144,11 +157,34 @@ Preset intent:
144
157
  | Preset | Intent |
145
158
  |---|---|
146
159
  | `minimal` | Academic research skills only, no MCP records. |
147
- | `default` | Academic research skills plus core scholarly MCP records. |
160
+ | `default` | Academic research skills plus the low-friction arXiv MCP record. |
148
161
  | `enhanced` | `default` plus curated external complementary skill bundles. |
149
- | `literature` | SOTA and systematic-review work with citation-library MCP records. |
150
- | `writing` | Paper-writing and Overleaf-oriented work. |
151
- | `full` | Broad optional connector and specialist setup. |
162
+ | `literature` | SOTA and systematic-review work with arXiv plus DBLP for computer science bibliography. |
163
+ | `writing` | Paper-writing work; Overleaf is documented as an opt-in credentialed integration. |
164
+ | `full` | Broad setup with low-friction arXiv and DBLP records plus the full optional MCP catalog documented. |
165
+
166
+ MCP defaults are intentionally conservative. Semantic Scholar, OpenAlex,
167
+ Zotero, Overleaf, Crossref, and fallback aggregators are useful, but they need
168
+ API keys, local apps, manual setup, or source-policy review. Enable them with
169
+ `npx academic-research mcp enable <server>` after reading
170
+ `docs/agent/mcp-setup.md`, use `npx academic-research mcp env <server>` to see
171
+ runtime prerequisites, then run `npx academic-research mcp doctor`.
172
+
173
+ The MCP catalog distinguishes local runtime adapters from hosted endpoints and
174
+ manual integrations. arXiv and DBLP are low-friction local `uvx` runtimes.
175
+ Semantic Scholar is useful for citation graphs but works best with
176
+ `SEMANTIC_SCHOLAR_API_KEY`. OpenAlex requires `OPENALEX_API_KEY` for the
177
+ selected local adapter; OpenAlex keys are free for normal academic use with
178
+ daily free usage. PubMed is a biomedical-specific `npx` runtime and remains
179
+ opt-in. Zotero needs the local Zotero desktop app and Zoty setup. Overleaf is
180
+ manual and credentialed. Crossref and broad paper-search aggregators are kept
181
+ as fallback/manual entries until a project explicitly needs them.
182
+
183
+ Generated MCP snippets are project documentation and client-ready config, not
184
+ live tools by themselves. Your MCP client must load the generated snippet, and
185
+ the referenced commands must be available on your machine or runnable through
186
+ `uvx`/`npx`. `mcp install` only runs finite setup commands such as the arXiv
187
+ tool install; it deliberately does not launch stdio MCP servers.
152
188
 
153
189
  ## Validate This Package
154
190
 
@@ -166,8 +202,8 @@ Releases are tag-driven. Update `package.json` and `package-lock.json`, commit
166
202
  the change, create `vX.Y.Z`, and push the tag:
167
203
 
168
204
  ```bash
169
- git tag -a v0.1.4 -m "v0.1.4"
170
- git push origin main v0.1.4
205
+ git tag -a v0.1.6 -m "v0.1.6"
206
+ git push origin main v0.1.6
171
207
  ```
172
208
 
173
209
  Once the GitHub repository is public, the release workflow validates the tag
@@ -0,0 +1,9 @@
1
+ export declare const DEFAULT_AGENT = "universal";
2
+ export declare const AUTO_AGENT = "auto";
3
+ export declare const SUPPORTED_SKILL_AGENT_TARGETS: readonly ["adal", "aider-desk", "amp", "antigravity", "augment", "bob", "claude-code", "cline", "codearts-agent", "codebuddy", "codemaker", "codestudio", "codex", "command-code", "continue", "cortex", "crush", "cursor", "deepagents", "devin", "dexto", "droid", "firebender", "forgecode", "gemini-cli", "github-copilot", "goose", "hermes-agent", "iflow-cli", "junie", "kilo", "kimi-cli", "kiro-cli", "kode", "mcpjam", "mistral-vibe", "mux", "neovate", "openclaw", "opencode", "openhands", "pi", "pochi", "qoder", "qwen-code", "replit", "roo", "rovodev", "tabnine-cli", "trae", "trae-cn", "universal", "warp", "windsurf", "zencoder"];
4
+ export declare const AGENT_TARGET_ALIASES: Record<string, string>;
5
+ export declare function normalizeAgentTarget(agent: string | undefined): string;
6
+ export declare function assertKnownAgentTarget(agent: string | undefined): string;
7
+ export declare function formatAgentTargetList(): string;
8
+ export declare function formatSupportedAgentTargetLines(indent?: string, width?: number): string[];
9
+ export declare function formatAgentAliasLines(indent?: string): string[];
@@ -0,0 +1,126 @@
1
+ export const DEFAULT_AGENT = "universal";
2
+ export const AUTO_AGENT = "auto";
3
+ export const SUPPORTED_SKILL_AGENT_TARGETS = [
4
+ "adal",
5
+ "aider-desk",
6
+ "amp",
7
+ "antigravity",
8
+ "augment",
9
+ "bob",
10
+ "claude-code",
11
+ "cline",
12
+ "codearts-agent",
13
+ "codebuddy",
14
+ "codemaker",
15
+ "codestudio",
16
+ "codex",
17
+ "command-code",
18
+ "continue",
19
+ "cortex",
20
+ "crush",
21
+ "cursor",
22
+ "deepagents",
23
+ "devin",
24
+ "dexto",
25
+ "droid",
26
+ "firebender",
27
+ "forgecode",
28
+ "gemini-cli",
29
+ "github-copilot",
30
+ "goose",
31
+ "hermes-agent",
32
+ "iflow-cli",
33
+ "junie",
34
+ "kilo",
35
+ "kimi-cli",
36
+ "kiro-cli",
37
+ "kode",
38
+ "mcpjam",
39
+ "mistral-vibe",
40
+ "mux",
41
+ "neovate",
42
+ "openclaw",
43
+ "opencode",
44
+ "openhands",
45
+ "pi",
46
+ "pochi",
47
+ "qoder",
48
+ "qwen-code",
49
+ "replit",
50
+ "roo",
51
+ "rovodev",
52
+ "tabnine-cli",
53
+ "trae",
54
+ "trae-cn",
55
+ "universal",
56
+ "warp",
57
+ "windsurf",
58
+ "zencoder"
59
+ ];
60
+ export const AGENT_TARGET_ALIASES = {
61
+ claude: "claude-code",
62
+ claude_code: "claude-code"
63
+ };
64
+ const SUPPORTED_AGENT_TARGETS = new Set([
65
+ AUTO_AGENT,
66
+ ...SUPPORTED_SKILL_AGENT_TARGETS
67
+ ]);
68
+ export function normalizeAgentTarget(agent) {
69
+ const value = agent?.trim();
70
+ if (!value)
71
+ return DEFAULT_AGENT;
72
+ const normalized = value.toLowerCase();
73
+ return AGENT_TARGET_ALIASES[normalized] ?? normalized;
74
+ }
75
+ export function assertKnownAgentTarget(agent) {
76
+ const normalized = normalizeAgentTarget(agent);
77
+ if (!SUPPORTED_AGENT_TARGETS.has(normalized)) {
78
+ const value = agent?.trim() || DEFAULT_AGENT;
79
+ throw new Error([
80
+ `unknown agent target: ${value}`,
81
+ `Use ${DEFAULT_AGENT}, ${AUTO_AGENT}, or one supported skills.sh agent id.`,
82
+ "List targets with: npx -p create-academic-research academic-research agents list",
83
+ `Supported ids: ${specificAgentTargets().join(", ")}`,
84
+ `Aliases: ${formatAgentAliasesInline()}`
85
+ ].join("\n"));
86
+ }
87
+ return normalized;
88
+ }
89
+ export function formatAgentTargetList() {
90
+ const lines = [
91
+ `${DEFAULT_AGENT}\tRecommended shared project-local .agents/skills copy`,
92
+ `${AUTO_AGENT}\tLet the skills CLI detect installed agents; may create multiple agent-specific copies`,
93
+ ...specificAgentTargets().map((agent) => `${agent}\tskills.sh agent id`),
94
+ ...Object.entries(AGENT_TARGET_ALIASES).map(([alias, target]) => `alias\t${alias}\t${target}`)
95
+ ];
96
+ return `${lines.join("\n")}\n`;
97
+ }
98
+ export function formatSupportedAgentTargetLines(indent = " ", width = 100) {
99
+ const labels = specificAgentTargets();
100
+ const lines = [];
101
+ let current = indent;
102
+ for (const label of labels) {
103
+ const next = current === indent ? label : `, ${label}`;
104
+ if (current.length + next.length > width) {
105
+ lines.push(current);
106
+ current = `${indent}${label}`;
107
+ }
108
+ else {
109
+ current += next;
110
+ }
111
+ }
112
+ if (current.trim())
113
+ lines.push(current);
114
+ return lines;
115
+ }
116
+ export function formatAgentAliasLines(indent = " ") {
117
+ return Object.entries(AGENT_TARGET_ALIASES).map(([alias, target]) => `${indent}${alias} -> ${target}`);
118
+ }
119
+ function formatAgentAliasesInline() {
120
+ return Object.entries(AGENT_TARGET_ALIASES)
121
+ .map(([alias, target]) => `${alias} -> ${target}`)
122
+ .join(", ");
123
+ }
124
+ function specificAgentTargets() {
125
+ return SUPPORTED_SKILL_AGENT_TARGETS.filter((agent) => agent !== DEFAULT_AGENT);
126
+ }
@@ -1,6 +1,7 @@
1
+ import { DEFAULT_AGENT, SUPPORTED_SKILL_AGENT_TARGETS } from "./agents.js";
1
2
  import { type Runner } from "./runner.js";
2
3
  import { type McpToolCommandKey } from "./stack.js";
3
- export declare const DEFAULT_AGENT = "universal";
4
+ export { DEFAULT_AGENT, SUPPORTED_SKILL_AGENT_TARGETS };
4
5
  export interface CapabilityState {
5
6
  agent: string;
6
7
  preset: string;
@@ -51,4 +52,3 @@ export declare function installMcpTools(root: string, servers: string[], runner?
51
52
  export declare function uninstallMcpTools(root: string, servers: string[], runner?: Runner): Promise<CapabilityCommandResult>;
52
53
  export declare function doctorMcpServers(root: string): Promise<McpDoctorResult>;
53
54
  export declare function assertKnownMcpServers(servers: string[]): void;
54
- export {};
@@ -1,10 +1,10 @@
1
1
  import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { join, relative } from "node:path";
3
3
  import YAML from "yaml";
4
+ import { assertKnownAgentTarget, AUTO_AGENT, DEFAULT_AGENT, normalizeAgentTarget, SUPPORTED_SKILL_AGENT_TARGETS } from "./agents.js";
4
5
  import { defaultRunner } from "./runner.js";
5
6
  import { AGENT_STACK, presetMcpServers } from "./stack.js";
6
- export const DEFAULT_AGENT = "universal";
7
- const AUTO_AGENT = "auto";
7
+ export { DEFAULT_AGENT, SUPPORTED_SKILL_AGENT_TARGETS };
8
8
  export async function readCapabilities(root) {
9
9
  try {
10
10
  return readCapabilitiesFile(root);
@@ -18,13 +18,14 @@ export async function readCapabilities(root) {
18
18
  }
19
19
  export async function writeCapabilities(root, state) {
20
20
  const next = {
21
- agent: normalizeAgent(state.agent),
21
+ agent: assertKnownAgentTarget(state.agent),
22
22
  preset: state.preset ?? "default",
23
23
  scope: "project-local",
24
24
  mcp_servers: [...(state.mcp_servers ?? [])]
25
25
  };
26
26
  await writeFile(join(root, "configs/capabilities.yaml"), YAML.stringify(next), "utf8");
27
27
  await writeCapabilityProfile(root, next);
28
+ await writeMcpSetup(root, next);
28
29
  await writeMcpSnippet(root, next);
29
30
  await appendCapabilityLog(root, next);
30
31
  }
@@ -42,7 +43,7 @@ export async function buildSkillInstallCommands(root, preset = "default", option
42
43
  if (!selected)
43
44
  throw new Error(`unknown skill preset: ${preset}`);
44
45
  const state = await readCapabilities(root);
45
- const agent = normalizeAgent(options.agent ?? state.agent);
46
+ const agent = assertKnownAgentTarget(options.agent ?? state.agent);
46
47
  const commands = [];
47
48
  for (const bundleName of selected.skill_bundles) {
48
49
  const bundle = AGENT_STACK.skill_bundles[bundleName];
@@ -61,7 +62,7 @@ export async function buildSkillInstallCommands(root, preset = "default", option
61
62
  }
62
63
  export async function installSkills(root, preset = "default", options = {}, runner = defaultRunner) {
63
64
  const state = await readCapabilities(root);
64
- const agent = normalizeAgent(options.agent ?? state.agent);
65
+ const agent = assertKnownAgentTarget(options.agent ?? state.agent);
65
66
  const commands = await buildSkillInstallCommands(root, preset, options);
66
67
  for (const command of commands) {
67
68
  await runner.run(command, { cwd: root });
@@ -135,7 +136,7 @@ export async function enableMcpServers(root, servers, options = {}) {
135
136
  const selected = dedupe([...(state.mcp_servers ?? []), ...servers]);
136
137
  await writeCapabilities(root, {
137
138
  ...state,
138
- agent: options.agent ?? state.agent,
139
+ agent: assertKnownAgentTarget(options.agent ?? state.agent),
139
140
  mcp_servers: selected
140
141
  });
141
142
  return { ok: true, servers: selected };
@@ -147,7 +148,7 @@ export async function disableMcpServers(root, servers, options = {}) {
147
148
  const selected = (state.mcp_servers ?? []).filter((server) => !blocked.has(server));
148
149
  await writeCapabilities(root, {
149
150
  ...state,
150
- agent: options.agent ?? state.agent,
151
+ agent: assertKnownAgentTarget(options.agent ?? state.agent),
151
152
  mcp_servers: selected
152
153
  });
153
154
  return { ok: true, servers: selected };
@@ -233,6 +234,19 @@ export async function doctorMcpServers(root) {
233
234
  const server = AGENT_STACK.mcp_servers[name];
234
235
  if (!server)
235
236
  continue;
237
+ for (const envName of server.required_env) {
238
+ if (!process.env[envName]) {
239
+ errors.push(`${name}: missing required environment variable: ${envName}`);
240
+ }
241
+ }
242
+ for (const envName of server.recommended_env) {
243
+ if (!process.env[envName]) {
244
+ warnings.push(`${name}: recommended environment variable not set: ${envName}`);
245
+ }
246
+ }
247
+ if (server.local_service) {
248
+ warnings.push(`${name}: requires local service: ${server.local_service}`);
249
+ }
236
250
  if (!server.command) {
237
251
  warnings.push(`${name}: manual setup only; no generated client command`);
238
252
  continue;
@@ -240,9 +254,6 @@ export async function doctorMcpServers(root) {
240
254
  if (!generatedServers.has(name)) {
241
255
  errors.push(`${name}: enabled but missing from generated MCP snippet`);
242
256
  }
243
- if (!server.install_command) {
244
- warnings.push(`${name}: no automated install command is defined`);
245
- }
246
257
  }
247
258
  return { ok: errors.length === 0, errors, warnings, enabled };
248
259
  }
@@ -269,7 +280,7 @@ async function writeCapabilityProfile(root, state) {
269
280
  const lines = [
270
281
  "# Agent Capability Profile",
271
282
  "",
272
- `- Agent target: \`${normalizeAgent(state.agent)}\``,
283
+ `- Agent target: \`${assertKnownAgentTarget(state.agent)}\``,
273
284
  `- Preset: \`${state.preset ?? "default"}\``,
274
285
  "- Scope: `project-local`",
275
286
  "",
@@ -290,13 +301,46 @@ async function writeCapabilityProfile(root, state) {
290
301
  else {
291
302
  for (const name of state.mcp_servers) {
292
303
  const server = AGENT_STACK.mcp_servers[name];
293
- const status = server?.command ? "generated config" : "manual setup";
304
+ const status = server?.command ? server.execution_mode : "manual setup";
294
305
  lines.push(`- \`${name}\` (${status}): ${server?.smoke_test ?? "Smoke-test before use."}`);
295
306
  }
296
307
  }
297
308
  lines.push("", "## Rules", "", "- Skill installation is project-local by default.", "- Agent target `universal` installs one shared project-local `.agents/skills` copy.", "- MCP enable/disable changes project records; install/uninstall changes external tools.", "- Keep API keys, tokens, cookies, and browser sessions out of git.", "- Cite repository source records, not raw MCP output alone.", "");
298
309
  await writeFile(join(root, "docs/agent/capability-profile.md"), lines.join("\n"), "utf8");
299
310
  }
311
+ async function writeMcpSetup(root, state) {
312
+ const enabled = new Set(state.mcp_servers ?? []);
313
+ const lines = [
314
+ "# MCP Setup",
315
+ "",
316
+ "This file is generated from the project-local academic research capability stack.",
317
+ "MCP records are configuration snippets and setup guidance; the active MCP client must load the generated snippet before these servers become live tools.",
318
+ "",
319
+ "## Enabled MCP Servers",
320
+ ""
321
+ ];
322
+ if (enabled.size === 0) {
323
+ lines.push("- None.");
324
+ }
325
+ else {
326
+ for (const name of state.mcp_servers) {
327
+ const server = AGENT_STACK.mcp_servers[name];
328
+ if (!server)
329
+ continue;
330
+ lines.push(`- \`${name}\` (${server.readiness}, ${server.priority}): ${server.source_need}`, ` - Source: \`${server.source}\``, ` - Execution mode: \`${server.execution_mode}\``, ...(server.hosted_url ? [` - Hosted endpoint: <${server.hosted_url}>`] : []), ` - Runtime: ${formatRuntime(server.command, server.args)}`, ` - Install command: ${server.install_command ? `\`${server.install_command}\`` : "none; runtime-only or manual setup"}`, ...server.setup_commands.map((command) => ` - Setup command: \`${command}\``), ` - Smoke test: ${server.smoke_test}`, ` - Risks: ${server.risks}`);
331
+ appendMcpPrerequisiteLines(lines, server.required_env, server.recommended_env, server.local_service);
332
+ }
333
+ }
334
+ lines.push("", "## Available MCP Catalog", "");
335
+ for (const [name, server] of Object.entries(AGENT_STACK.mcp_servers)) {
336
+ const status = enabled.has(name) ? "enabled" : "available";
337
+ 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}\``));
338
+ appendMcpPrerequisiteLines(lines, server.required_env, server.recommended_env, server.local_service);
339
+ }
340
+ 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.", "");
341
+ await mkdir(join(root, "docs/agent"), { recursive: true });
342
+ await writeFile(join(root, "docs/agent/mcp-setup.md"), lines.join("\n"), "utf8");
343
+ }
300
344
  async function appendCapabilityLog(root, state) {
301
345
  const logPath = join(root, "wiki/log.md");
302
346
  const date = new Date().toISOString().slice(0, 10);
@@ -307,10 +351,24 @@ function dedupe(values) {
307
351
  return [...new Set(values)];
308
352
  }
309
353
  function renderSkillCommand(command, agent) {
310
- const normalized = normalizeAgent(agent);
354
+ const normalized = assertKnownAgentTarget(agent);
311
355
  const agentFlag = normalized === AUTO_AGENT ? "" : `--agent '${normalized}'`;
312
356
  return command.replaceAll("{agent_flag}", agentFlag).replaceAll("{agent}", normalized);
313
357
  }
358
+ function appendMcpPrerequisiteLines(lines, requiredEnv, recommendedEnv, localService) {
359
+ if (requiredEnv.length > 0)
360
+ lines.push(` - Requires env: ${requiredEnv.map((name) => `\`${name}\``).join(", ")}`);
361
+ if (recommendedEnv.length > 0) {
362
+ lines.push(` - Recommended env: ${recommendedEnv.map((name) => `\`${name}\``).join(", ")}`);
363
+ }
364
+ if (localService)
365
+ lines.push(` - Local prerequisite: ${localService}`);
366
+ }
367
+ function formatRuntime(command, args) {
368
+ if (!command)
369
+ return "manual setup";
370
+ return `\`${[command, ...args].join(" ")}\``;
371
+ }
314
372
  async function readCapabilitiesFile(root) {
315
373
  const path = join(root, "configs/capabilities.yaml");
316
374
  return normalizeCapabilityState(YAML.parse(await readFile(path, "utf8")));
@@ -352,7 +410,7 @@ function splitCommand(command) {
352
410
  function normalizeCapabilityState(value) {
353
411
  const record = typeof value === "object" && value !== null ? value : {};
354
412
  return {
355
- agent: normalizeAgent(typeof record.agent === "string" ? record.agent : undefined),
413
+ agent: assertKnownAgentTarget(typeof record.agent === "string" ? record.agent : undefined),
356
414
  preset: typeof record.preset === "string" ? record.preset : "default",
357
415
  scope: "project-local",
358
416
  mcp_servers: Array.isArray(record.mcp_servers)
@@ -397,12 +455,8 @@ async function removeSkillsFromLock(root, skills) {
397
455
  await writeFile(path, `${JSON.stringify(record, null, 2)}\n`, "utf8");
398
456
  }
399
457
  }
400
- function normalizeAgent(agent) {
401
- const value = agent?.trim();
402
- return value ? value : DEFAULT_AGENT;
403
- }
404
458
  function mcpSnippetFileName(agent) {
405
- const normalized = normalizeAgent(agent);
459
+ const normalized = normalizeAgentTarget(agent);
406
460
  return normalized === DEFAULT_AGENT || normalized === AUTO_AGENT ? "mcp.json" : `${normalized}-mcp.json`;
407
461
  }
408
462
  async function removeInactiveMcpSnippets(outputDir, activeFile) {
package/dist/src/cli.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { basename, dirname, join, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- import { disableMcpServers, doctorMcpServers, enableMcpServers, DEFAULT_AGENT, installMcpTools, installSkills, listInstalledSkills, mcpToolCommandTexts, readCapabilities, removeSkills, uninstallMcpTools, updateSkills } from "./capabilities.js";
4
+ import { assertKnownMcpServers, disableMcpServers, doctorMcpServers, enableMcpServers, DEFAULT_AGENT, installMcpTools, installSkills, listInstalledSkills, 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";
8
+ import { formatAgentAliasLines, formatAgentTargetList, formatSupportedAgentTargetLines } from "./agents.js";
8
9
  import { packageify, slugify, titleFromSlug } from "./names.js";
9
10
  const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
10
11
  const packageVersion = readPackageVersion();
@@ -101,6 +102,8 @@ async function lifecycleMain(argv) {
101
102
  return doctorCommand(argv.slice(1));
102
103
  if (command === "rename")
103
104
  return renameCommand(argv.slice(1));
105
+ if (command === "agents")
106
+ return agentsCommand(argv.slice(1));
104
107
  if (command === "skills")
105
108
  return skillsCommand(argv.slice(1));
106
109
  if (command === "mcp")
@@ -137,6 +140,21 @@ async function renameCommand(argv) {
137
140
  console.log(`Renamed project to ${result.slug}`);
138
141
  return 0;
139
142
  }
143
+ async function agentsCommand(argv) {
144
+ const subcommand = argv[0] ?? "list";
145
+ const parsed = parseFlags(argv.slice(1), ROOT_FLAGS);
146
+ if (subcommand === "help" || subcommand === "--help" || subcommand === "-h" || flagBool(parsed.flags, "help")) {
147
+ printAgentsHelp();
148
+ return 0;
149
+ }
150
+ if (subcommand === "list") {
151
+ assertOnlyOptions(parsed.flags, "agents list", []);
152
+ assertNoArguments(parsed.positionals, "agents list");
153
+ process.stdout.write(formatAgentTargetList());
154
+ return 0;
155
+ }
156
+ throw new Error(`unknown agents command: ${subcommand}`);
157
+ }
140
158
  async function skillsCommand(argv) {
141
159
  const subcommand = argv[0] ?? "list";
142
160
  const parsed = parseFlags(argv.slice(1), SKILLS_FLAGS);
@@ -226,10 +244,10 @@ async function mcpCommand(argv) {
226
244
  assertNoArguments(parsed.positionals, "mcp list");
227
245
  const state = await readCapabilities(root);
228
246
  const enabled = new Set(state.mcp_servers ?? []);
247
+ console.log("status\tid\treadiness\texecution_mode\tdescription");
229
248
  for (const [name, server] of Object.entries(AGENT_STACK.mcp_servers)) {
230
249
  const status = enabled.has(name) ? "enabled" : "available";
231
- const installer = mcpInstallMode(server.install_command, server.command);
232
- console.log(`${status}\t${name}\t${server.source_need}\t${installer}`);
250
+ console.log(`${status}\t${name}\t${server.readiness}\t${server.execution_mode}\t${server.source_need}`);
233
251
  }
234
252
  return 0;
235
253
  }
@@ -245,9 +263,9 @@ async function mcpCommand(argv) {
245
263
  if (subcommand === "available") {
246
264
  assertOnlyOptions(parsed.flags, "mcp available", []);
247
265
  assertNoArguments(parsed.positionals, "mcp available");
266
+ console.log("id\treadiness\texecution_mode\tdescription");
248
267
  for (const [name, server] of Object.entries(AGENT_STACK.mcp_servers)) {
249
- const installer = mcpInstallMode(server.install_command, server.command);
250
- console.log(`${name}\t${server.source_need}\t${installer}`);
268
+ console.log(`${name}\t${server.readiness}\t${server.execution_mode}\t${server.source_need}`);
251
269
  }
252
270
  return 0;
253
271
  }
@@ -260,6 +278,15 @@ async function mcpCommand(argv) {
260
278
  console.log(command);
261
279
  return 0;
262
280
  }
281
+ if (subcommand === "env") {
282
+ assertOnlyOptions(parsed.flags, "mcp env", ["root"]);
283
+ const root = resolve(flagString(parsed.flags, "root") ?? ".");
284
+ const selected = parsed.positionals.length > 0 ? parsed.positionals : (await readCapabilities(root)).mcp_servers;
285
+ assertKnownMcpServers(selected);
286
+ console.log("id\ttype\tvalue");
287
+ printMcpEnvironment(selected);
288
+ return 0;
289
+ }
263
290
  if (subcommand === "enable") {
264
291
  assertOnlyOptions(parsed.flags, "mcp enable", ["root", "agent"]);
265
292
  const root = resolve(flagString(parsed.flags, "root") ?? ".");
@@ -387,11 +414,6 @@ function assertOnlyOptions(flags, command, allowedOptions) {
387
414
  throw new Error(`${command} does not accept ${unexpected.map((name) => `--${name}`).join(", ")}`);
388
415
  }
389
416
  }
390
- function mcpInstallMode(installCommand, runtimeCommand) {
391
- if (installCommand)
392
- return installCommand;
393
- return runtimeCommand ? "runtime-only" : "manual";
394
- }
395
417
  export function formatInteractiveCreateGuide() {
396
418
  const presetLines = Object.entries(AGENT_STACK.presets).map(([name, preset]) => ` ${name.padEnd(10)} ${preset.description}`);
397
419
  return [
@@ -401,15 +423,24 @@ export function formatInteractiveCreateGuide() {
401
423
  ...presetLines,
402
424
  "",
403
425
  "Agent target:",
404
- " universal Default. Installs one shared project-local .agents/skills copy.",
405
- " codex Codex-specific project-local skill install.",
406
- " auto Lets the upstream skills CLI detect agents; may install to multiple agent loaders.",
426
+ " universal Recommended. One shared project-local .agents/skills copy.",
427
+ " auto Let the skills CLI detect installed agents; may create multiple agent-specific copies.",
428
+ " <id> Any supported skills.sh agent id.",
429
+ "",
430
+ "Supported specific agent ids:",
431
+ ...formatSupportedAgentTargetLines(),
432
+ "",
433
+ "Aliases:",
434
+ ...formatAgentAliasLines(),
407
435
  "",
408
436
  "Skill and MCP behavior:",
409
437
  " Skills are copied into the project, not installed globally.",
410
438
  " MCP records are written into configs/capabilities.yaml and docs/agent/generated/.",
439
+ " docs/agent/mcp-setup.md records enabled servers, optional catalog entries, env vars, and smoke tests.",
440
+ " default enables only low-friction arXiv; credentialed/local services are opt-in.",
411
441
  " MCP installers are optional and run only finite installer commands.",
412
- " runtime-only MCP servers are configured for the MCP client but have no install step.",
442
+ " MCP execution modes are explicit: uvx-runtime, npx-runtime, local-service, manual, or fallback.",
443
+ " Use `academic-research mcp env <server>` to inspect env vars and local prerequisites.",
413
444
  ""
414
445
  ].join("\n");
415
446
  }
@@ -426,7 +457,7 @@ function printCreateHelp() {
426
457
  " --package <name> Python package name. Default: normalized project name.",
427
458
  " --preset <name> Capability preset: minimal, default, enhanced, literature, writing, full.",
428
459
  " --profile <name> Project profile metadata. Default: academic-general.",
429
- " --agent <name> Agent target. Default: universal.",
460
+ " --agent <id> Agent target: universal, auto, or a supported skills.sh id.",
430
461
  " --install-skills Install project-local skills without prompting.",
431
462
  " --no-install-skills Skip project-local skill installation.",
432
463
  " --install-mcp-tools Run finite external MCP install commands after creation.",
@@ -445,7 +476,7 @@ function printMissingTargetHelp() {
445
476
  }
446
477
  function printLifecycleHelp() {
447
478
  console.log([
448
- "Usage: academic-research <doctor|rename|skills|mcp>",
479
+ "Usage: academic-research <doctor|rename|agents|skills|mcp>",
449
480
  "",
450
481
  "Manage a generated academic research repository after creation.",
451
482
  "",
@@ -454,6 +485,21 @@ function printLifecycleHelp() {
454
485
  " -v, --version Show package version."
455
486
  ].join("\n"));
456
487
  }
488
+ function printAgentsHelp() {
489
+ console.log([
490
+ "Usage: academic-research agents <list>",
491
+ "",
492
+ "List supported project-local agent targets.",
493
+ "",
494
+ "Targets:",
495
+ " universal Recommended shared project-local .agents/skills copy.",
496
+ " auto Let the skills CLI detect installed agents.",
497
+ " <id> A supported skills.sh agent id.",
498
+ "",
499
+ "Options:",
500
+ " -h, --help Show this help."
501
+ ].join("\n"));
502
+ }
457
503
  function printSkillsHelp() {
458
504
  console.log([
459
505
  "Usage: academic-research skills <list|status|presets|install|remove|uninstall|update> [options]",
@@ -463,22 +509,50 @@ function printSkillsHelp() {
463
509
  "Options:",
464
510
  " --root <path> Project root for list, status, install, remove, uninstall, update.",
465
511
  " --preset <name> Capability preset for install.",
466
- " --agent <name> Agent selector for install. Default: project capability agent.",
512
+ " --agent <id> Agent selector for install. Default: project capability agent.",
467
513
  " -h, --help Show this help."
468
514
  ].join("\n"));
469
515
  }
470
516
  function printMcpHelp() {
471
517
  console.log([
472
- "Usage: academic-research mcp <list|enabled|available|commands|enable|disable|install|uninstall|doctor> [servers...]",
518
+ "Usage: academic-research mcp <list|enabled|available|commands|env|enable|disable|install|uninstall|doctor> [servers...]",
473
519
  "",
474
520
  "Manage MCP records and finite external MCP tool installs.",
475
521
  "",
476
522
  "Options:",
477
523
  " --root <path> Project root for project-state commands.",
478
- " --agent <name> Agent for enable/disable generated snippets.",
524
+ " --agent <id> Agent for enable/disable generated snippets.",
479
525
  " -h, --help Show this help."
480
526
  ].join("\n"));
481
527
  }
528
+ function printMcpEnvironment(servers) {
529
+ for (const name of servers) {
530
+ const server = AGENT_STACK.mcp_servers[name];
531
+ let wroteLine = false;
532
+ for (const envName of server.required_env) {
533
+ console.log(`${name}\trequired\t${envName}`);
534
+ wroteLine = true;
535
+ }
536
+ for (const envName of server.recommended_env) {
537
+ console.log(`${name}\trecommended\t${envName}`);
538
+ wroteLine = true;
539
+ }
540
+ if (server.hosted_url) {
541
+ console.log(`${name}\thosted-endpoint\t${server.hosted_url}`);
542
+ wroteLine = true;
543
+ }
544
+ if (server.local_service) {
545
+ console.log(`${name}\tlocal-service\t${server.local_service}`);
546
+ wroteLine = true;
547
+ }
548
+ for (const command of server.setup_commands) {
549
+ console.log(`${name}\tsetup-command\t${command}`);
550
+ wroteLine = true;
551
+ }
552
+ if (!wroteLine)
553
+ console.log(`${name}\tnone\t-`);
554
+ }
555
+ }
482
556
  function readPackageVersion() {
483
557
  try {
484
558
  const packageJson = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
@@ -3,6 +3,7 @@ import { basename, dirname, join, resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import YAML from "yaml";
5
5
  import { DEFAULT_AGENT, initializeCapabilities, installSkills } from "./capabilities.js";
6
+ import { assertKnownAgentTarget } from "./agents.js";
6
7
  import { copyDirectory, exists, isNonEmptyDirectory, movePath, readJson, writeJson } from "./files.js";
7
8
  import { packageify, slugify, titleFromSlug } from "./names.js";
8
9
  import { AGENT_STACK } from "./stack.js";
@@ -90,7 +91,7 @@ export async function createProject(options) {
90
91
  const slug = slugify(options.slug ?? title);
91
92
  const packageName = packageify(options.packageName ?? slug);
92
93
  const preset = options.preset ?? "default";
93
- const agent = options.agent ?? DEFAULT_AGENT;
94
+ const agent = assertKnownAgentTarget(options.agent ?? DEFAULT_AGENT);
94
95
  if (!AGENT_STACK.presets[preset]) {
95
96
  throw new Error(`unknown capability preset: ${preset}. Expected one of: ${Object.keys(AGENT_STACK.presets).join(", ")}`);
96
97
  }
@@ -135,6 +136,7 @@ export async function doctorProject(root) {
135
136
  "configs/agent-stack.yaml",
136
137
  "configs/capabilities.yaml",
137
138
  "docs/agent/capability-profile.md",
139
+ "docs/agent/mcp-setup.md",
138
140
  "docs/agent/generated",
139
141
  "sources/source-ledger.csv",
140
142
  "sota/literature-matrix.csv",
@@ -205,7 +207,7 @@ async function writeGeneratedPackageJson(root, { slug }) {
205
207
  const path = join(root, "package.json");
206
208
  const data = await readJson(path);
207
209
  const existingSpec = data.devDependencies?.["create-academic-research"];
208
- const packageSpec = process.env.CREATE_ACADEMIC_RESEARCH_PACKAGE_SPEC ?? existingSpec ?? "0.1.4";
210
+ const packageSpec = process.env.CREATE_ACADEMIC_RESEARCH_PACKAGE_SPEC ?? existingSpec ?? "0.1.6";
209
211
  data.name = slug;
210
212
  data.devDependencies = {
211
213
  ...(data.devDependencies ?? {}),
@@ -9,7 +9,8 @@ export async function askCreateOptions(defaults, locks = {}) {
9
9
  const preset = await question(rl, "Capability preset", defaults.preset);
10
10
  const agent = await question(rl, "Agent target", defaults.agent);
11
11
  const installSkills = locks.installSkills ?? (await yesNo(rl, "Install project-local skills now", defaults.installSkills));
12
- const installMcpTools = locks.installMcpTools ?? (await yesNo(rl, "Run external MCP installers now", defaults.installMcpTools));
12
+ const installMcpTools = locks.installMcpTools ??
13
+ (await yesNo(rl, "Run finite external MCP tool installers now", defaults.installMcpTools));
13
14
  return { title, slug, packageName, preset, agent, installSkills, installMcpTools };
14
15
  }
15
16
  finally {
@@ -8,13 +8,21 @@ export interface CapabilityPreset {
8
8
  mcp_servers: string[];
9
9
  }
10
10
  export interface McpServer {
11
+ readiness: string;
11
12
  priority: string;
13
+ execution_mode: string;
12
14
  source_need: string;
15
+ source: string;
16
+ hosted_url: string;
13
17
  install_command: string;
14
18
  uninstall_command: string;
19
+ setup_commands: string[];
15
20
  command: string;
16
21
  args: string[];
17
22
  env: Record<string, string>;
23
+ required_env: string[];
24
+ recommended_env: string[];
25
+ local_service: string;
18
26
  smoke_test: string;
19
27
  risks: string;
20
28
  }
package/dist/src/stack.js CHANGED
@@ -20,38 +20,6 @@ export const AGENT_STACK = {
20
20
  commands: [
21
21
  "npm exec --yes --package skills -- skills add existential-birds/beagle {agent_flag} --skill docling --copy -y"
22
22
  ]
23
- },
24
- optional_connectors: {
25
- description: "CLI-side literature connectors to use only when MCP servers are unavailable.",
26
- commands: [
27
- "npm exec --yes --package skills -- skills add davila7/claude-code-templates {agent_flag} --skill openalex-database --copy -y",
28
- "npm exec --yes --package skills -- skills add agents365-ai/365-skills {agent_flag} --skill semanticscholar-skill --copy -y",
29
- "npm exec --yes --package skills -- skills add fuzhiyu/researchprojecttemplate {agent_flag} --skill zotero-paper-reader --copy -y"
30
- ]
31
- },
32
- optional_mechanical_specialists: {
33
- description: "Narrow mechanical helpers that do not replace project-native governance.",
34
- commands: [
35
- "npm exec --yes --package skills -- skills add bahayonghang/academic-writing-skills {agent_flag} --skill latex-paper-en --copy -y",
36
- [
37
- "npm exec --yes --package skills -- skills add lllllllama/ai-paper-reproduction-skill",
38
- "{agent_flag}",
39
- "--skill",
40
- [
41
- "ai-research-reproduction",
42
- "repo-intake-and-plan",
43
- "env-and-assets-bootstrap",
44
- "minimal-run-and-audit",
45
- "paper-context-resolver",
46
- "analyze-project",
47
- "safe-debug",
48
- "run-train",
49
- "explore-run",
50
- "explore-code"
51
- ].join(" "),
52
- "--copy -y"
53
- ].join(" ")
54
- ]
55
23
  }
56
24
  },
57
25
  presets: {
@@ -61,119 +29,205 @@ export const AGENT_STACK = {
61
29
  mcp_servers: []
62
30
  },
63
31
  default: {
64
- description: "Clean academic research setup with core scholarly MCP records.",
32
+ description: "Clean academic research setup with the low-friction arXiv MCP record.",
65
33
  skill_bundles: ["academic_research"],
66
- mcp_servers: ["arxiv", "semantic-scholar", "openalex"]
34
+ mcp_servers: ["arxiv"]
67
35
  },
68
36
  enhanced: {
69
37
  description: "Default academic setup plus complementary agent engineering, document, frontend, testing, and doc conversion skills.",
70
38
  skill_bundles: ["academic_research", "default_complementary", "docling"],
71
- mcp_servers: ["arxiv", "semantic-scholar", "openalex"]
39
+ mcp_servers: ["arxiv"]
72
40
  },
73
41
  literature: {
74
- description: "Literature-heavy SOTA, survey, scoping, or systematic review setup.",
42
+ description: "Literature-heavy SOTA, survey, scoping, or systematic review setup with CS bibliography support.",
75
43
  skill_bundles: ["academic_research", "default_complementary", "docling"],
76
- mcp_servers: ["arxiv", "semantic-scholar", "openalex", "crossref", "zotero"]
44
+ mcp_servers: ["arxiv", "dblp"]
77
45
  },
78
46
  writing: {
79
- description: "Paper-writing and Overleaf-oriented setup.",
80
- skill_bundles: [
81
- "academic_research",
82
- "default_complementary",
83
- "docling",
84
- "optional_mechanical_specialists"
85
- ],
86
- mcp_servers: ["arxiv", "semantic-scholar", "crossref", "overleaf"]
47
+ description: "Paper-writing setup with Overleaf documented as an opt-in credentialed integration.",
48
+ skill_bundles: ["academic_research", "default_complementary", "docling"],
49
+ mcp_servers: ["arxiv"]
87
50
  },
88
51
  full: {
89
- description: "Broad setup with optional connector and mechanical specialist skills.",
90
- skill_bundles: [
91
- "academic_research",
92
- "default_complementary",
93
- "docling",
94
- "optional_connectors",
95
- "optional_mechanical_specialists"
96
- ],
97
- mcp_servers: ["arxiv", "semantic-scholar", "openalex", "crossref", "pubmed", "zotero", "overleaf"]
52
+ description: "Broad setup with low-friction scholarly MCP records plus the full optional MCP catalog documented.",
53
+ skill_bundles: ["academic_research", "default_complementary", "docling"],
54
+ mcp_servers: ["arxiv", "dblp"]
98
55
  }
99
56
  },
100
57
  mcp_servers: {
101
58
  arxiv: {
59
+ readiness: "low-friction",
102
60
  priority: "default",
61
+ execution_mode: "uvx-runtime",
103
62
  source_need: "arXiv search, download, and local paper reading.",
63
+ source: "blazickjp/arxiv-mcp-server",
64
+ hosted_url: "",
104
65
  install_command: "uv tool install 'arxiv-mcp-server[pdf]'",
105
66
  uninstall_command: "uv tool uninstall arxiv-mcp-server",
106
- command: "arxiv-mcp-server",
107
- args: [],
67
+ setup_commands: [],
68
+ command: "uvx",
69
+ args: ["--from", "arxiv-mcp-server[pdf]", "arxiv-mcp-server"],
108
70
  env: {},
71
+ required_env: [],
72
+ recommended_env: [],
73
+ local_service: "",
109
74
  smoke_test: "For a computer science project, search one CS query, download one known paper, and read it locally.",
110
75
  risks: "Respect arXiv rate limits; paper text is untrusted input."
111
76
  },
112
77
  "semantic-scholar": {
113
- priority: "default",
78
+ readiness: "credential-recommended",
79
+ priority: "optional",
80
+ execution_mode: "uvx-runtime",
114
81
  source_need: "Semantic Scholar papers, citations, authors, and recommendations.",
82
+ source: "akapet00/semantic-scholar-mcp",
83
+ hosted_url: "",
115
84
  install_command: "",
116
85
  uninstall_command: "",
86
+ setup_commands: [],
117
87
  command: "uvx",
118
88
  args: ["--from", "git+https://github.com/akapet00/semantic-scholar-mcp", "semantic-scholar-mcp"],
119
- env: { SEMANTIC_SCHOLAR_API_KEY: "${SEMANTIC_SCHOLAR_API_KEY}" },
89
+ env: {},
90
+ required_env: [],
91
+ recommended_env: ["SEMANTIC_SCHOLAR_API_KEY"],
120
92
  smoke_test: "Search one known title, then fetch citations or references.",
121
- risks: "API key recommended for sustained work; metadata can be incomplete."
93
+ local_service: "",
94
+ risks: "API key recommended for sustained work and to avoid shared-pool rate limits; metadata can be incomplete."
122
95
  },
123
96
  openalex: {
124
- priority: "default",
97
+ readiness: "credential-required",
98
+ priority: "optional",
99
+ execution_mode: "npx-runtime",
125
100
  source_need: "OpenAlex broad scholarly graph.",
101
+ source: "cyanheads/openalex-mcp-server",
102
+ hosted_url: "https://openalex.caseyjhand.com/mcp",
126
103
  install_command: "",
127
104
  uninstall_command: "",
105
+ setup_commands: [],
128
106
  command: "npx",
129
- args: ["-y", "@cyanheads/openalex-mcp-server"],
130
- env: { OPENALEX_API_KEY: "${OPENALEX_API_KEY_OR_EMAIL}" },
107
+ args: ["-y", "@cyanheads/openalex-mcp-server@latest"],
108
+ env: {},
109
+ required_env: ["OPENALEX_API_KEY"],
110
+ recommended_env: [],
111
+ local_service: "",
131
112
  smoke_test: "Search works by title or DOI and confirm stable OpenAlex IDs.",
132
- risks: "Smoke-test before relying on it for final coverage."
113
+ 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."
133
114
  },
134
115
  crossref: {
116
+ readiness: "manual",
135
117
  priority: "manual",
118
+ execution_mode: "manual",
136
119
  source_need: "DOI and publication metadata.",
120
+ source: "manual selection required; candidate: AiAgentKarl/crossref-academic-mcp-server",
121
+ hosted_url: "",
137
122
  install_command: "",
138
123
  uninstall_command: "",
124
+ setup_commands: [],
139
125
  command: "",
140
126
  args: [],
141
127
  env: {},
128
+ required_env: [],
129
+ recommended_env: [],
130
+ local_service: "",
142
131
  smoke_test: "Resolve one DOI into publication metadata after choosing a maintained local server.",
143
- risks: "Manual integration only; do not generate placeholder paths."
132
+ risks: "Manual integration only; current zero-friction Crossref-only MCP candidates are less mature than arXiv, DBLP, PubMed, or OpenAlex."
144
133
  },
145
134
  pubmed: {
135
+ readiness: "domain-specific",
146
136
  priority: "domain-specific",
137
+ execution_mode: "npx-runtime",
147
138
  source_need: "PubMed and biomedical literature.",
139
+ source: "cyanheads/pubmed-mcp-server",
140
+ hosted_url: "https://pubmed.caseyjhand.com/mcp",
148
141
  install_command: "",
149
142
  uninstall_command: "",
143
+ setup_commands: [],
150
144
  command: "npx",
151
- args: ["-y", "@cyanheads/pubmed-mcp-server"],
152
- env: { NCBI_API_KEY: "${NCBI_API_KEY}" },
145
+ args: ["-y", "@cyanheads/pubmed-mcp-server@latest"],
146
+ env: { MCP_TRANSPORT_TYPE: "stdio", MCP_LOG_LEVEL: "warning" },
147
+ required_env: [],
148
+ recommended_env: ["NCBI_API_KEY", "NCBI_ADMIN_EMAIL"],
149
+ local_service: "",
153
150
  smoke_test: "Search one PMID or title and fetch metadata.",
154
- risks: "Domain-specific; observe NCBI rate limits."
151
+ risks: "Domain-specific; observe NCBI rate limits. API key and contact email improve reliability."
152
+ },
153
+ dblp: {
154
+ readiness: "low-friction-cs",
155
+ priority: "cs",
156
+ execution_mode: "uvx-runtime",
157
+ source_need: "DBLP computer science bibliography, venues, authors, and BibTeX.",
158
+ source: "szeider/mcp-dblp",
159
+ hosted_url: "",
160
+ install_command: "",
161
+ uninstall_command: "",
162
+ setup_commands: [],
163
+ command: "uvx",
164
+ args: ["mcp-dblp"],
165
+ env: {},
166
+ required_env: [],
167
+ recommended_env: [],
168
+ local_service: "",
169
+ smoke_test: "Search one known CS paper title and export or inspect its DBLP BibTeX.",
170
+ risks: "CS-specific metadata source; use with arXiv/Semantic Scholar/OpenAlex for coverage beyond DBLP."
155
171
  },
156
172
  zotero: {
173
+ readiness: "local-service",
157
174
  priority: "local-library",
175
+ execution_mode: "local-service",
158
176
  source_need: "Zotero local library and attachments.",
177
+ source: "eric-tramel/zoty",
178
+ hosted_url: "",
159
179
  install_command: "",
160
180
  uninstall_command: "",
181
+ setup_commands: ["uvx --refresh zoty setup", "uvx --refresh zoty doctor"],
161
182
  command: "uvx",
162
183
  args: ["zoty", "mcp"],
163
184
  env: {},
185
+ required_env: [],
186
+ recommended_env: [],
187
+ local_service: "Zotero desktop running with local API enabled; Zoty Bridge required for attachment and collection write operations.",
164
188
  smoke_test: "List collections, search one known item, and export BibTeX.",
165
189
  risks: "Requires Zotero local API and bridge setup."
166
190
  },
167
191
  overleaf: {
192
+ readiness: "manual-credentialed",
168
193
  priority: "writing",
194
+ execution_mode: "manual-local",
169
195
  source_need: "Overleaf project sync.",
170
- install_command: "uv tool install overleaf-mcp-server",
171
- uninstall_command: "uv tool uninstall overleaf-mcp-server",
172
- command: "overleaf-mcp",
173
- args: ["serve"],
174
- env: { OVERLEAF_TOKEN: "${OVERLEAF_TOKEN}" },
196
+ source: "YounesBensafia/overleaf-mcp-server",
197
+ hosted_url: "",
198
+ install_command: "",
199
+ uninstall_command: "",
200
+ setup_commands: [],
201
+ command: "",
202
+ args: [],
203
+ env: {},
204
+ required_env: ["OVERLEAF_TOKEN"],
205
+ recommended_env: ["PROJECT_ID"],
206
+ local_service: "Local clone of the Overleaf MCP server configured with uv and an Overleaf project that supports Git sync.",
175
207
  smoke_test: "List projects or read one .tex file; do not write by default.",
176
- risks: "Requires token setup; write access needs explicit approval."
208
+ risks: "Requires token and project setup; write/push access needs explicit approval."
209
+ },
210
+ "paper-search": {
211
+ readiness: "fallback",
212
+ priority: "fallback",
213
+ execution_mode: "manual-fallback",
214
+ source_need: "Multi-source paper search and download fallback across many scholarly sources.",
215
+ source: "openags/paper-search-mcp",
216
+ hosted_url: "",
217
+ install_command: "",
218
+ uninstall_command: "",
219
+ setup_commands: [],
220
+ command: "",
221
+ args: [],
222
+ env: {},
223
+ required_env: [],
224
+ recommended_env: [
225
+ "PAPER_SEARCH_MCP_UNPAYWALL_EMAIL",
226
+ "PAPER_SEARCH_MCP_SEMANTIC_SCHOLAR_API_KEY"
227
+ ],
228
+ local_service: "Manual review required before enabling; configure only permitted sources.",
229
+ smoke_test: "Search one harmless query with a source allow-list and verify provenance for each result.",
230
+ risks: "Powerful aggregator with optional restricted-source workflows; keep Sci-Hub or questionable download features disabled unless explicitly accepted."
177
231
  }
178
232
  }
179
233
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-academic-research",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Create and manage agent-ready academic research repositories.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -53,23 +53,30 @@ Project-local skills and MCP records are managed with:
53
53
 
54
54
  ```bash
55
55
  npx academic-research skills presets
56
+ npx academic-research agents list
56
57
  npx academic-research skills install --preset default
57
58
  npx academic-research skills install --preset enhanced
58
59
  npx academic-research skills list
59
60
  npx academic-research skills status
60
- npx academic-research mcp enable arxiv semantic-scholar openalex
61
61
  npx academic-research mcp list
62
+ npx academic-research mcp env openalex semantic-scholar zotero
63
+ npx academic-research mcp enable arxiv dblp
62
64
  npx academic-research mcp commands arxiv
63
65
  npx academic-research mcp install arxiv
66
+ npx academic-research mcp doctor
64
67
  ```
65
68
 
66
69
  `skills list` reports installed project-local skills. `skills presets` reports
67
70
  available install presets. `mcp enable` changes project records. `mcp commands`
68
- prints finite external install commands without running them. `mcp install`
69
- runs only finite tool installation commands; runtime-only `uvx`/`npx` MCP
70
- servers may have no install step and are started later by the MCP client.
71
+ prints finite external install commands without running them. `mcp env` prints
72
+ env vars, hosted endpoints, local prerequisites, and setup commands before you
73
+ enable optional servers. `mcp install` runs only finite tool installation
74
+ commands; runtime-only `uvx`/`npx` MCP servers may have no install step and are
75
+ started later by the MCP client.
71
76
 
72
77
  `default` installs the companion academic research skill package and keeps the
73
- MCP records focused on core scholarly discovery. `enhanced` adds complementary
74
- external skills for agent engineering, frontend work, testing, document
75
- formats, and PDF conversion.
78
+ MCP records focused on low-friction arXiv discovery. `literature` and `full`
79
+ add DBLP for computer science bibliography. Credentialed, local-service, or
80
+ domain-specific MCP servers such as OpenAlex, Semantic Scholar, PubMed, Zotero,
81
+ and Overleaf should be enabled only after reading `docs/agent/mcp-setup.md` and
82
+ checking their prerequisites with `mcp env`.
@@ -9,9 +9,10 @@
9
9
  "skills:presets": "academic-research skills presets",
10
10
  "mcp:list": "academic-research mcp list",
11
11
  "mcp:commands": "academic-research mcp commands",
12
+ "mcp:env": "academic-research mcp env",
12
13
  "mcp:doctor": "academic-research mcp doctor"
13
14
  },
14
15
  "devDependencies": {
15
- "create-academic-research": "0.1.4"
16
+ "create-academic-research": "0.1.6"
16
17
  }
17
18
  }