create-academic-research 0.1.7 → 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 +17 -9
- package/dist/src/capabilities.d.ts +18 -0
- package/dist/src/capabilities.js +140 -1
- package/dist/src/cli.js +82 -23
- package/dist/src/project.js +5 -2
- package/dist/src/stack.d.ts +6 -0
- package/dist/src/stack.js +71 -1
- package/package.json +1 -1
- package/template/.env.example +23 -0
- package/template/AGENTS.md +2 -0
- package/template/README.md +7 -0
- package/template/docs/agent/mcp-client-setup.md +43 -0
- package/template/docs/agent/mcp-setup.md +4 -0
- package/template/package.json +2 -1
package/README.md
CHANGED
|
@@ -93,6 +93,7 @@ npx academic-research agents list
|
|
|
93
93
|
npx academic-research skills presets
|
|
94
94
|
npx academic-research skills install --preset default
|
|
95
95
|
npx academic-research skills install --preset enhanced
|
|
96
|
+
npx academic-research skills install source-ingestion sota-literature-review
|
|
96
97
|
npx academic-research skills list
|
|
97
98
|
npx academic-research skills status
|
|
98
99
|
npx academic-research skills remove source-ingestion
|
|
@@ -103,6 +104,7 @@ npx academic-research mcp enabled
|
|
|
103
104
|
npx academic-research mcp available
|
|
104
105
|
npx academic-research mcp commands arxiv
|
|
105
106
|
npx academic-research mcp env openalex semantic-scholar zotero
|
|
107
|
+
npx academic-research mcp env --dotenv --all > .env.example
|
|
106
108
|
npx academic-research mcp enable arxiv dblp
|
|
107
109
|
npx academic-research mcp disable arxiv
|
|
108
110
|
npx academic-research mcp install arxiv
|
|
@@ -122,7 +124,7 @@ Skills are project-local by default.
|
|
|
122
124
|
| Command | Meaning |
|
|
123
125
|
|---|---|
|
|
124
126
|
| `skills presets` | List available capability presets. |
|
|
125
|
-
| `skills install` | Install project-local skills
|
|
127
|
+
| `skills install` | Install project-local skills by preset, or selected skill ids such as `source-ingestion`. This does not change MCP records. |
|
|
126
128
|
| `skills list` | List skills found in project-local skill loader directories. |
|
|
127
129
|
| `skills status` | Show configured project preset, agent, scope, skill roots, unique skill ids, and installed copies. |
|
|
128
130
|
| `skills remove` / `skills uninstall` | Remove selected project-local skills. |
|
|
@@ -136,7 +138,7 @@ MCP commands are split by side-effect:
|
|
|
136
138
|
| `mcp enabled` | List only enabled MCP server ids. |
|
|
137
139
|
| `mcp available` | List the local MCP catalog. |
|
|
138
140
|
| `mcp commands` | Print finite external install commands without running them. Runtime-only `uvx`/`npx` servers may have no install command. |
|
|
139
|
-
| `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`. |
|
|
140
142
|
| `mcp enable` | Enable an MCP server in project records and generated snippets. |
|
|
141
143
|
| `mcp disable` | Remove an MCP server from project records and generated snippets. |
|
|
142
144
|
| `mcp install` | Run finite external tool install commands for selected MCP servers. It must not launch stdio MCP servers. |
|
|
@@ -184,11 +186,17 @@ The MCP catalog distinguishes local runtime adapters from hosted endpoints and
|
|
|
184
186
|
manual integrations. arXiv and DBLP are low-friction local `uvx` runtimes.
|
|
185
187
|
Semantic Scholar is useful for citation graphs but works best with
|
|
186
188
|
`SEMANTIC_SCHOLAR_API_KEY`. OpenAlex requires `OPENALEX_API_KEY` for the
|
|
187
|
-
selected local adapter; OpenAlex keys are free
|
|
188
|
-
|
|
189
|
-
opt-in. Zotero needs the local
|
|
190
|
-
|
|
191
|
-
|
|
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.
|
|
192
200
|
|
|
193
201
|
Generated MCP snippets are project documentation and client-ready config, not
|
|
194
202
|
live tools by themselves. Your MCP client must load the generated snippet, and
|
|
@@ -215,8 +223,8 @@ Releases are tag-driven. Update `package.json` and `package-lock.json`, commit
|
|
|
215
223
|
the change, create `vX.Y.Z`, and push the tag:
|
|
216
224
|
|
|
217
225
|
```bash
|
|
218
|
-
git tag -a v0.1.
|
|
219
|
-
git push origin main v0.1.
|
|
226
|
+
git tag -a v0.1.9 -m "v0.1.9"
|
|
227
|
+
git push origin main v0.1.9
|
|
220
228
|
```
|
|
221
229
|
|
|
222
230
|
Once the GitHub repository is public, the release workflow validates the tag
|
|
@@ -16,6 +16,7 @@ export interface InitializeCapabilitiesOptions {
|
|
|
16
16
|
export interface CapabilityCommandResult {
|
|
17
17
|
ok: true;
|
|
18
18
|
count?: number;
|
|
19
|
+
skills?: string[];
|
|
19
20
|
servers?: string[];
|
|
20
21
|
}
|
|
21
22
|
export interface InstalledSkill {
|
|
@@ -29,14 +30,23 @@ export interface McpDoctorResult {
|
|
|
29
30
|
warnings: string[];
|
|
30
31
|
enabled: string[];
|
|
31
32
|
}
|
|
33
|
+
export interface McpEnvironmentEntry {
|
|
34
|
+
server: string;
|
|
35
|
+
kind: "required" | "recommended" | "default";
|
|
36
|
+
name: string;
|
|
37
|
+
value: string;
|
|
38
|
+
}
|
|
32
39
|
interface SkillInstallOptions {
|
|
33
40
|
agent?: string;
|
|
34
41
|
}
|
|
35
42
|
export declare function readCapabilities(root: string): Promise<CapabilityState>;
|
|
36
43
|
export declare function writeCapabilities(root: string, state: Partial<CapabilityState>): Promise<void>;
|
|
44
|
+
export declare function writeMcpEnvironmentExample(root: string): Promise<void>;
|
|
37
45
|
export declare function initializeCapabilities(root: string, options?: InitializeCapabilitiesOptions): Promise<void>;
|
|
38
46
|
export declare function buildSkillInstallCommands(root: string, preset?: string, options?: SkillInstallOptions): Promise<string[][]>;
|
|
47
|
+
export declare function buildExplicitSkillInstallCommands(root: string, skills: string[], options?: SkillInstallOptions): Promise<string[][]>;
|
|
39
48
|
export declare function installSkills(root: string, preset?: string, options?: SkillInstallOptions, runner?: Runner): Promise<CapabilityCommandResult>;
|
|
49
|
+
export declare function installSkillIds(root: string, skills: string[], options?: SkillInstallOptions, runner?: Runner): Promise<CapabilityCommandResult>;
|
|
40
50
|
export declare function listInstalledSkills(root: string): Promise<InstalledSkill[]>;
|
|
41
51
|
export declare function removeSkills(root: string, skills: string[], runner?: Runner): Promise<CapabilityCommandResult>;
|
|
42
52
|
export declare function updateSkills(root: string, runner?: Runner): Promise<CapabilityCommandResult>;
|
|
@@ -48,6 +58,14 @@ export declare function disableMcpServers(root: string, servers: string[], optio
|
|
|
48
58
|
}): Promise<CapabilityCommandResult>;
|
|
49
59
|
export declare function mcpToolCommands(servers: string[], key?: McpToolCommandKey): string[][];
|
|
50
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;
|
|
51
69
|
export declare function installMcpTools(root: string, servers: string[], runner?: Runner): Promise<CapabilityCommandResult>;
|
|
52
70
|
export declare function uninstallMcpTools(root: string, servers: string[], runner?: Runner): Promise<CapabilityCommandResult>;
|
|
53
71
|
export declare function doctorMcpServers(root: string): Promise<McpDoctorResult>;
|
package/dist/src/capabilities.js
CHANGED
|
@@ -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);
|
|
@@ -60,6 +63,21 @@ export async function buildSkillInstallCommands(root, preset = "default", option
|
|
|
60
63
|
}
|
|
61
64
|
return commands;
|
|
62
65
|
}
|
|
66
|
+
export async function buildExplicitSkillInstallCommands(root, skills, options = {}) {
|
|
67
|
+
const selectedSkills = normalizeSkillIds(skills);
|
|
68
|
+
const state = await readCapabilities(root);
|
|
69
|
+
const agent = assertKnownAgentTarget(options.agent ?? state.agent);
|
|
70
|
+
const skillsBySource = new Map();
|
|
71
|
+
for (const skill of selectedSkills) {
|
|
72
|
+
const source = skillSourceForId(skill);
|
|
73
|
+
if (!source)
|
|
74
|
+
throw new Error(`unknown skill id: ${skill}`);
|
|
75
|
+
const sourceSkills = skillsBySource.get(source) ?? [];
|
|
76
|
+
sourceSkills.push(skill);
|
|
77
|
+
skillsBySource.set(source, sourceSkills);
|
|
78
|
+
}
|
|
79
|
+
return [...skillsBySource.entries()].map(([source, sourceSkills]) => skillAddCommand(source, sourceSkills, agent));
|
|
80
|
+
}
|
|
63
81
|
export async function installSkills(root, preset = "default", options = {}, runner = defaultRunner) {
|
|
64
82
|
const state = await readCapabilities(root);
|
|
65
83
|
const agent = assertKnownAgentTarget(options.agent ?? state.agent);
|
|
@@ -76,6 +94,22 @@ export async function installSkills(root, preset = "default", options = {}, runn
|
|
|
76
94
|
}
|
|
77
95
|
return { ok: true, count: commands.length };
|
|
78
96
|
}
|
|
97
|
+
export async function installSkillIds(root, skills, options = {}, runner = defaultRunner) {
|
|
98
|
+
const state = await readCapabilities(root);
|
|
99
|
+
const agent = assertKnownAgentTarget(options.agent ?? state.agent);
|
|
100
|
+
const selectedSkills = normalizeSkillIds(skills);
|
|
101
|
+
const commands = await buildExplicitSkillInstallCommands(root, selectedSkills, options);
|
|
102
|
+
for (const command of commands) {
|
|
103
|
+
await runner.run(command, { cwd: root });
|
|
104
|
+
}
|
|
105
|
+
if (state.agent !== agent) {
|
|
106
|
+
await writeCapabilities(root, {
|
|
107
|
+
...state,
|
|
108
|
+
agent
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return { ok: true, count: commands.length, skills: selectedSkills };
|
|
112
|
+
}
|
|
79
113
|
export async function listInstalledSkills(root) {
|
|
80
114
|
const roots = await discoverProjectSkillRoots(root);
|
|
81
115
|
const skills = [];
|
|
@@ -173,6 +207,50 @@ export function mcpToolCommandTexts(servers, key = "install_command") {
|
|
|
173
207
|
}
|
|
174
208
|
return commands;
|
|
175
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
|
+
}
|
|
176
254
|
export async function installMcpTools(root, servers, runner = defaultRunner) {
|
|
177
255
|
const selected = servers.length > 0 ? servers : (await readCapabilities(root)).mcp_servers ?? [];
|
|
178
256
|
assertKnownMcpServers(selected);
|
|
@@ -287,6 +365,7 @@ async function writeCapabilityProfile(root, state) {
|
|
|
287
365
|
"## Skills",
|
|
288
366
|
"",
|
|
289
367
|
`- Install with: \`academic-research skills install --preset ${state.preset ?? "default"}\``,
|
|
368
|
+
"- Install selected skills with: `academic-research skills install <skill-id> [...]`",
|
|
290
369
|
"- List installed with: `academic-research skills list`",
|
|
291
370
|
"- List presets with: `academic-research skills presets`",
|
|
292
371
|
"- Remove with: `academic-research skills remove <skill>`",
|
|
@@ -337,7 +416,7 @@ async function writeMcpSetup(root, state) {
|
|
|
337
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}\``));
|
|
338
417
|
appendMcpPrerequisiteLines(lines, server.required_env, server.recommended_env, server.local_service);
|
|
339
418
|
}
|
|
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.", "");
|
|
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.", "");
|
|
341
420
|
await mkdir(join(root, "docs/agent"), { recursive: true });
|
|
342
421
|
await writeFile(join(root, "docs/agent/mcp-setup.md"), lines.join("\n"), "utf8");
|
|
343
422
|
}
|
|
@@ -350,11 +429,71 @@ async function appendCapabilityLog(root, state) {
|
|
|
350
429
|
function dedupe(values) {
|
|
351
430
|
return [...new Set(values)];
|
|
352
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
|
+
}
|
|
353
450
|
function renderSkillCommand(command, agent) {
|
|
354
451
|
const normalized = assertKnownAgentTarget(agent);
|
|
355
452
|
const agentFlag = normalized === AUTO_AGENT ? "" : `--agent '${normalized}'`;
|
|
356
453
|
return command.replaceAll("{agent_flag}", agentFlag).replaceAll("{agent}", normalized);
|
|
357
454
|
}
|
|
455
|
+
function skillAddCommand(source, skills, agent) {
|
|
456
|
+
return [
|
|
457
|
+
"npm",
|
|
458
|
+
"exec",
|
|
459
|
+
"--yes",
|
|
460
|
+
"--package",
|
|
461
|
+
"skills",
|
|
462
|
+
"--",
|
|
463
|
+
"skills",
|
|
464
|
+
"add",
|
|
465
|
+
source,
|
|
466
|
+
...skillAgentArgs(agent),
|
|
467
|
+
"--skill",
|
|
468
|
+
...skills,
|
|
469
|
+
"--copy",
|
|
470
|
+
"-y"
|
|
471
|
+
];
|
|
472
|
+
}
|
|
473
|
+
function skillAgentArgs(agent) {
|
|
474
|
+
const normalized = assertKnownAgentTarget(agent);
|
|
475
|
+
return normalized === AUTO_AGENT ? [] : ["--agent", normalized];
|
|
476
|
+
}
|
|
477
|
+
function normalizeSkillIds(skills) {
|
|
478
|
+
if (skills.length === 0)
|
|
479
|
+
throw new Error("no skills selected");
|
|
480
|
+
const result = [];
|
|
481
|
+
for (const skill of skills) {
|
|
482
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/.test(skill)) {
|
|
483
|
+
throw new Error(`invalid skill id: ${skill}`);
|
|
484
|
+
}
|
|
485
|
+
if (!result.includes(skill))
|
|
486
|
+
result.push(skill);
|
|
487
|
+
}
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
function skillSourceForId(skill) {
|
|
491
|
+
for (const source of Object.values(AGENT_STACK.skill_sources)) {
|
|
492
|
+
if (source.skills.includes(skill))
|
|
493
|
+
return source.source;
|
|
494
|
+
}
|
|
495
|
+
return undefined;
|
|
496
|
+
}
|
|
358
497
|
function appendMcpPrerequisiteLines(lines, requiredEnv, recommendedEnv, localService) {
|
|
359
498
|
if (requiredEnv.length > 0)
|
|
360
499
|
lines.push(` - Requires env: ${requiredEnv.map((name) => `\`${name}\``).join(", ")}`);
|
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, 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";
|
|
@@ -9,11 +9,19 @@ import { formatAgentAliasLines, formatAgentTargetList, formatSupportedAgentTarge
|
|
|
9
9
|
import { packageify, slugify, titleFromSlug } from "./names.js";
|
|
10
10
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
11
11
|
const packageVersion = readPackageVersion();
|
|
12
|
-
const CREATE_FLAGS = flagSchema([
|
|
12
|
+
const CREATE_FLAGS = flagSchema([
|
|
13
|
+
"yes",
|
|
14
|
+
"help",
|
|
15
|
+
"version",
|
|
16
|
+
"install-skills",
|
|
17
|
+
"no-install-skills",
|
|
18
|
+
"install-mcp-tools",
|
|
19
|
+
"no-install-mcp-tools"
|
|
20
|
+
], ["title", "slug", "package", "preset", "profile", "agent"]);
|
|
13
21
|
const ROOT_FLAGS = flagSchema(["help"], ["root"]);
|
|
14
22
|
const RENAME_FLAGS = flagSchema(["help"], ["root", "title", "slug", "package"]);
|
|
15
23
|
const SKILLS_FLAGS = flagSchema(["help"], ["root", "preset", "agent"]);
|
|
16
|
-
const MCP_FLAGS = flagSchema(["help"], ["root", "agent"]);
|
|
24
|
+
const MCP_FLAGS = flagSchema(["help", "all", "dotenv", "required", "recommended"], ["root", "agent"]);
|
|
17
25
|
export async function main(argv = process.argv.slice(2), mode = "create") {
|
|
18
26
|
try {
|
|
19
27
|
if (mode === "create")
|
|
@@ -43,6 +51,9 @@ async function createMain(argv) {
|
|
|
43
51
|
if (flagBool(parsed.flags, "install-skills") && flagBool(parsed.flags, "no-install-skills")) {
|
|
44
52
|
throw new Error("cannot use --install-skills and --no-install-skills together");
|
|
45
53
|
}
|
|
54
|
+
if (flagBool(parsed.flags, "install-mcp-tools") && flagBool(parsed.flags, "no-install-mcp-tools")) {
|
|
55
|
+
throw new Error("cannot use --install-mcp-tools and --no-install-mcp-tools together");
|
|
56
|
+
}
|
|
46
57
|
if (parsed.positionals.length > 1) {
|
|
47
58
|
throw new Error(`unexpected argument: ${parsed.positionals[1]}`);
|
|
48
59
|
}
|
|
@@ -60,7 +71,9 @@ async function createMain(argv) {
|
|
|
60
71
|
const installSkillsLock = flagBool(parsed.flags, "install-skills") || flagBool(parsed.flags, "no-install-skills")
|
|
61
72
|
? defaults.installSkills
|
|
62
73
|
: undefined;
|
|
63
|
-
const installMcpToolsLock = flagBool(parsed.flags, "install-mcp-tools")
|
|
74
|
+
const installMcpToolsLock = flagBool(parsed.flags, "install-mcp-tools") || flagBool(parsed.flags, "no-install-mcp-tools")
|
|
75
|
+
? defaults.installMcpTools
|
|
76
|
+
: undefined;
|
|
64
77
|
const answers = interactive
|
|
65
78
|
? await askInteractiveCreateOptions(defaults, {
|
|
66
79
|
installSkills: installSkillsLock,
|
|
@@ -158,6 +171,7 @@ async function setupCommand(argv) {
|
|
|
158
171
|
console.log("academic-research skills status");
|
|
159
172
|
console.log("academic-research mcp list");
|
|
160
173
|
console.log("academic-research mcp env");
|
|
174
|
+
console.log("academic-research mcp env --dotenv --all");
|
|
161
175
|
console.log("academic-research mcp smoke");
|
|
162
176
|
console.log("academic-research doctor");
|
|
163
177
|
return project.ok ? 0 : 1;
|
|
@@ -240,8 +254,18 @@ async function skillsCommand(argv) {
|
|
|
240
254
|
if (subcommand === "install") {
|
|
241
255
|
assertOnlyOptions(parsed.flags, "skills install", ["root", "preset", "agent"]);
|
|
242
256
|
const root = resolve(flagString(parsed.flags, "root") ?? ".");
|
|
243
|
-
|
|
244
|
-
const
|
|
257
|
+
const explicitSkills = parsed.positionals;
|
|
258
|
+
const explicitPreset = flagString(parsed.flags, "preset");
|
|
259
|
+
if (explicitSkills.length > 0) {
|
|
260
|
+
if (explicitPreset)
|
|
261
|
+
throw new Error("skills install does not accept --preset when skill ids are provided");
|
|
262
|
+
const result = await installSkillIds(root, explicitSkills, {
|
|
263
|
+
agent: flagString(parsed.flags, "agent")
|
|
264
|
+
});
|
|
265
|
+
console.log(`Installed ${result.skills?.length ?? explicitSkills.length} skill(s) with ${result.count ?? 0} command(s).`);
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
const preset = explicitPreset ?? "default";
|
|
245
269
|
const result = await installSkills(root, preset, {
|
|
246
270
|
agent: flagString(parsed.flags, "agent")
|
|
247
271
|
});
|
|
@@ -316,12 +340,27 @@ async function mcpCommand(argv) {
|
|
|
316
340
|
return 0;
|
|
317
341
|
}
|
|
318
342
|
if (subcommand === "env") {
|
|
319
|
-
assertOnlyOptions(parsed.flags, "mcp env", ["root"]);
|
|
343
|
+
assertOnlyOptions(parsed.flags, "mcp env", ["root", "all", "dotenv", "required", "recommended"]);
|
|
320
344
|
const root = resolve(flagString(parsed.flags, "root") ?? ".");
|
|
321
|
-
|
|
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;
|
|
322
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
|
+
}
|
|
323
362
|
console.log("id\ttype\tvalue");
|
|
324
|
-
printMcpEnvironment(selected);
|
|
363
|
+
printMcpEnvironment(selected, filters);
|
|
325
364
|
return 0;
|
|
326
365
|
}
|
|
327
366
|
if (subcommand === "enable") {
|
|
@@ -496,6 +535,7 @@ export function formatInteractiveCreateGuide() {
|
|
|
496
535
|
" MCP installers are optional and run only finite installer commands.",
|
|
497
536
|
" MCP execution modes are explicit: uvx-runtime, npx-runtime, local-service, manual, or fallback.",
|
|
498
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.",
|
|
499
539
|
""
|
|
500
540
|
].join("\n");
|
|
501
541
|
}
|
|
@@ -516,6 +556,7 @@ function printCreateHelp() {
|
|
|
516
556
|
" --install-skills Install project-local skills without prompting.",
|
|
517
557
|
" --no-install-skills Skip project-local skill installation.",
|
|
518
558
|
" --install-mcp-tools Run finite external MCP install commands after creation.",
|
|
559
|
+
" --no-install-mcp-tools Skip finite external MCP install commands.",
|
|
519
560
|
" -h, --help Show this help.",
|
|
520
561
|
" -v, --version Show package version."
|
|
521
562
|
].join("\n"));
|
|
@@ -568,13 +609,17 @@ function printAgentsHelp() {
|
|
|
568
609
|
}
|
|
569
610
|
function printSkillsHelp() {
|
|
570
611
|
console.log([
|
|
571
|
-
"Usage: academic-research skills <list|status|presets|install|remove|uninstall|update> [options]",
|
|
612
|
+
"Usage: academic-research skills <list|status|presets|install|remove|uninstall|update> [skill-id...] [options]",
|
|
572
613
|
"",
|
|
573
614
|
"Manage project-local skill installs for a generated research repository.",
|
|
574
615
|
"",
|
|
616
|
+
"Examples:",
|
|
617
|
+
" academic-research skills install --preset default",
|
|
618
|
+
" academic-research skills install source-ingestion sota-literature-review",
|
|
619
|
+
"",
|
|
575
620
|
"Options:",
|
|
576
621
|
" --root <path> Project root for list, status, install, remove, uninstall, update.",
|
|
577
|
-
" --preset <name> Capability preset for install.",
|
|
622
|
+
" --preset <name> Capability preset for install when no skill ids are provided.",
|
|
578
623
|
" --agent <id> Agent selector for install. Default: project capability agent.",
|
|
579
624
|
" -h, --help Show this help."
|
|
580
625
|
].join("\n"));
|
|
@@ -585,9 +630,18 @@ function printMcpHelp() {
|
|
|
585
630
|
"",
|
|
586
631
|
"Manage MCP records, readiness checks, and finite external MCP tool installs.",
|
|
587
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
|
+
"",
|
|
588
638
|
"Options:",
|
|
589
639
|
" --root <path> Project root for project-state commands.",
|
|
590
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.",
|
|
591
645
|
" -h, --help Show this help."
|
|
592
646
|
].join("\n"));
|
|
593
647
|
}
|
|
@@ -617,29 +671,34 @@ function printMcpSmokeDiagnostics(servers) {
|
|
|
617
671
|
}
|
|
618
672
|
return failed;
|
|
619
673
|
}
|
|
620
|
-
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
|
+
}
|
|
621
681
|
for (const name of servers) {
|
|
622
682
|
const server = AGENT_STACK.mcp_servers[name];
|
|
683
|
+
const entries = grouped.get(name) ?? [];
|
|
623
684
|
let wroteLine = false;
|
|
624
|
-
for (const
|
|
625
|
-
console.log(`${name}\
|
|
685
|
+
for (const entry of entries) {
|
|
686
|
+
console.log(`${name}\t${entry.kind}\t${entry.name}${entry.value ? `=${entry.value}` : ""}`);
|
|
626
687
|
wroteLine = true;
|
|
627
688
|
}
|
|
628
|
-
|
|
629
|
-
console.log(`${name}\trecommended\t${envName}`);
|
|
630
|
-
wroteLine = true;
|
|
631
|
-
}
|
|
632
|
-
if (server.hosted_url) {
|
|
689
|
+
if (!options.requiredOnly && !options.recommendedOnly && server.hosted_url) {
|
|
633
690
|
console.log(`${name}\thosted-endpoint\t${server.hosted_url}`);
|
|
634
691
|
wroteLine = true;
|
|
635
692
|
}
|
|
636
|
-
if (server.local_service) {
|
|
693
|
+
if (!options.requiredOnly && !options.recommendedOnly && server.local_service) {
|
|
637
694
|
console.log(`${name}\tlocal-service\t${server.local_service}`);
|
|
638
695
|
wroteLine = true;
|
|
639
696
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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
|
+
}
|
|
643
702
|
}
|
|
644
703
|
if (!wroteLine)
|
|
645
704
|
console.log(`${name}\tnone\t-`);
|
package/dist/src/project.js
CHANGED
|
@@ -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.
|
|
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.d.ts
CHANGED
|
@@ -2,6 +2,11 @@ export interface SkillBundle {
|
|
|
2
2
|
description: string;
|
|
3
3
|
commands: string[];
|
|
4
4
|
}
|
|
5
|
+
export interface SkillSource {
|
|
6
|
+
description: string;
|
|
7
|
+
source: string;
|
|
8
|
+
skills: string[];
|
|
9
|
+
}
|
|
5
10
|
export interface CapabilityPreset {
|
|
6
11
|
description: string;
|
|
7
12
|
skill_bundles: string[];
|
|
@@ -30,6 +35,7 @@ export interface AgentStack {
|
|
|
30
35
|
version: number;
|
|
31
36
|
description: string;
|
|
32
37
|
skill_bundles: Record<string, SkillBundle>;
|
|
38
|
+
skill_sources: Record<string, SkillSource>;
|
|
33
39
|
presets: Record<string, CapabilityPreset>;
|
|
34
40
|
mcp_servers: Record<string, McpServer>;
|
|
35
41
|
}
|
package/dist/src/stack.js
CHANGED
|
@@ -22,6 +22,76 @@ export const AGENT_STACK = {
|
|
|
22
22
|
]
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
|
+
skill_sources: {
|
|
26
|
+
academic_research: {
|
|
27
|
+
description: "Academic research skills maintained by this project.",
|
|
28
|
+
source: "VincenzoImp/academic-research-skills",
|
|
29
|
+
skills: [
|
|
30
|
+
"academic-mcp-tooling",
|
|
31
|
+
"adversarial-peer-review",
|
|
32
|
+
"artifact-open-science",
|
|
33
|
+
"citation-bibliography-tooling",
|
|
34
|
+
"citation-claim-audit",
|
|
35
|
+
"cs-methodology-evaluation",
|
|
36
|
+
"cs-venue-strategy",
|
|
37
|
+
"document-conversion",
|
|
38
|
+
"ethics-data-governance",
|
|
39
|
+
"experiment-logbook",
|
|
40
|
+
"paper-writing-review",
|
|
41
|
+
"rebuttal-revision-strategy",
|
|
42
|
+
"repo-migration",
|
|
43
|
+
"research-data-analysis",
|
|
44
|
+
"research-design-positioning",
|
|
45
|
+
"research-project-maintenance",
|
|
46
|
+
"research-project-router",
|
|
47
|
+
"research-repo-reproduction",
|
|
48
|
+
"research-ui-prototyping",
|
|
49
|
+
"skill-evaluation",
|
|
50
|
+
"sota-literature-review",
|
|
51
|
+
"source-ingestion",
|
|
52
|
+
"systematic-review-prisma"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
superpowers: {
|
|
56
|
+
description: "General agent engineering skills from Superpowers.",
|
|
57
|
+
source: "obra/superpowers",
|
|
58
|
+
skills: [
|
|
59
|
+
"brainstorming",
|
|
60
|
+
"dispatching-parallel-agents",
|
|
61
|
+
"executing-plans",
|
|
62
|
+
"finishing-a-development-branch",
|
|
63
|
+
"receiving-code-review",
|
|
64
|
+
"requesting-code-review",
|
|
65
|
+
"subagent-driven-development",
|
|
66
|
+
"systematic-debugging",
|
|
67
|
+
"test-driven-development",
|
|
68
|
+
"using-git-worktrees",
|
|
69
|
+
"using-superpowers",
|
|
70
|
+
"verification-before-completion",
|
|
71
|
+
"writing-plans",
|
|
72
|
+
"writing-skills"
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
anthropics: {
|
|
76
|
+
description: "Document, frontend, testing, MCP, and skill-authoring helpers.",
|
|
77
|
+
source: "anthropics/skills",
|
|
78
|
+
skills: [
|
|
79
|
+
"docx",
|
|
80
|
+
"frontend-design",
|
|
81
|
+
"mcp-builder",
|
|
82
|
+
"pdf",
|
|
83
|
+
"pptx",
|
|
84
|
+
"skill-creator",
|
|
85
|
+
"webapp-testing",
|
|
86
|
+
"xlsx"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
docling: {
|
|
90
|
+
description: "Document conversion helper from the Beagle collection.",
|
|
91
|
+
source: "existential-birds/beagle",
|
|
92
|
+
skills: ["docling"]
|
|
93
|
+
}
|
|
94
|
+
},
|
|
25
95
|
presets: {
|
|
26
96
|
minimal: {
|
|
27
97
|
description: "Academic research skills only, no MCP records.",
|
|
@@ -110,7 +180,7 @@ export const AGENT_STACK = {
|
|
|
110
180
|
recommended_env: [],
|
|
111
181
|
local_service: "",
|
|
112
182
|
smoke_test: "Search works by title or DOI and confirm stable OpenAlex IDs.",
|
|
113
|
-
risks: "The selected local server requires OPENALEX_API_KEY. OpenAlex keys are free
|
|
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."
|
|
114
184
|
},
|
|
115
185
|
crossref: {
|
|
116
186
|
readiness: "manual",
|
package/package.json
CHANGED
|
@@ -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=
|
package/template/AGENTS.md
CHANGED
|
@@ -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
|
|
package/template/README.md
CHANGED
|
@@ -57,11 +57,13 @@ npx academic-research skills presets
|
|
|
57
57
|
npx academic-research agents list
|
|
58
58
|
npx academic-research skills install --preset default
|
|
59
59
|
npx academic-research skills install --preset enhanced
|
|
60
|
+
npx academic-research skills install source-ingestion sota-literature-review
|
|
60
61
|
npx academic-research skills list
|
|
61
62
|
npx academic-research skills status
|
|
62
63
|
npx academic-research setup
|
|
63
64
|
npx academic-research mcp list
|
|
64
65
|
npx academic-research mcp env openalex semantic-scholar zotero
|
|
66
|
+
npx academic-research mcp env --dotenv --all > .env.example
|
|
65
67
|
npx academic-research mcp enable arxiv dblp
|
|
66
68
|
npx academic-research mcp commands arxiv
|
|
67
69
|
npx academic-research mcp install arxiv
|
|
@@ -77,6 +79,11 @@ enable optional servers. `mcp install` runs only finite tool installation
|
|
|
77
79
|
commands; runtime-only `uvx`/`npx` MCP servers may have no install step and are
|
|
78
80
|
started later by the MCP client.
|
|
79
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
|
+
|
|
80
87
|
`setup` prints the current project capability state, installed skill counts,
|
|
81
88
|
enabled MCP records, and the next onboarding commands without changing files.
|
|
82
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`.
|
package/template/package.json
CHANGED
|
@@ -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.
|
|
19
|
+
"create-academic-research": "0.1.9"
|
|
19
20
|
}
|
|
20
21
|
}
|