@jam-mcp/server 1.4.5 → 1.4.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
@@ -74,10 +74,10 @@ auth login Store Jira credentials in this user's OS secret store
74
74
  runtime Show or change which JAM build this machine runs
75
75
  ```
76
76
 
77
- Written out, that is `npx --yes @jam-mcp/launcher@1.4.5 doctor`, or just `jam
77
+ Written out, that is `npx --yes @jam-mcp/launcher@1.4.6 doctor`, or just `jam
78
78
  doctor` if you took the launcher's optional global install. Starting from
79
79
  nothing — no install, no runtime chosen yet — use
80
- `npx --yes @jam-mcp/bootstrap@1.4.5 init` instead.
80
+ `npx --yes @jam-mcp/bootstrap@1.4.6 init` instead.
81
81
 
82
82
  Credentials come from the process environment or this user's OS secret store —
83
83
  never from a repository file — and never appear in logs, telemetry, or tool
@@ -13,7 +13,7 @@ import { LAUNCHER_PACKAGE_SPEC } from "@jam-mcp/launcher";
13
13
  export { LAUNCHER_PACKAGE_SPEC };
14
14
  export declare const JAM_MCP_ENTRY: {
15
15
  readonly command: "npx";
16
- readonly args: readonly ["--yes", "@jam-mcp/launcher@1.4.5", "serve"];
16
+ readonly args: readonly ["--yes", "@jam-mcp/launcher@1.4.6", "serve"];
17
17
  };
18
18
  /**
19
19
  * Recognise wiring from before the launcher existed: a hard-coded `node` path
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Reading Jira without the MCP channel.
3
+ *
4
+ * JAM's reads have lived only behind MCP tools. That is the right home when the
5
+ * agent's session already has them - but a session that registers JAM cannot be
6
+ * shown the new tools: Claude Code has no reload surface (`claude mcp` offers
7
+ * add / get / list / login / remove / reset-project-choices / serve, and nothing
8
+ * that re-reads registrations for a running session). So the agent that just
9
+ * installed JAM has to wait for its next session before it can read anything.
10
+ *
11
+ * What it did instead was worse: it answered Jira questions from whatever was at
12
+ * hand - git log, the code host, project documents - which is the exact
13
+ * substitution JAM exists to prevent.
14
+ *
15
+ * This is the same read, addressed differently. `search` / `context` / `full`
16
+ * call the same application functions the tools call, with the same deps, the
17
+ * same policies and the same `meta`. Nothing here re-implements a read, and
18
+ * nothing here is a second source of truth.
19
+ *
20
+ * Contract, enforced by tests:
21
+ * stdout - one JSON document and nothing else, no ANSI, no prompts
22
+ * stderr - diagnostics only
23
+ */
24
+ import { type BuildDepsOptions, type JamDeps } from "../deps.js";
25
+ export declare const JIRA_READ_USAGE = "Usage:\n jam jira search <jql> [--scope preview|complete]\n jam jira context <KEY> [KEY...]\n jam jira full <KEY> [KEY...]\n\nReads only. Output is one JSON document on stdout - the same result the MCP\ntools return, for a session that cannot see them yet.\n";
26
+ export type JiraReadOptions = BuildDepsOptions & {
27
+ /** Injected by tests so no test reaches a real Jira. */
28
+ deps?: JamDeps;
29
+ /** Where the JSON document goes. Defaults to stdout. */
30
+ write?: (text: string) => void;
31
+ /** Where diagnostics go. Defaults to stderr. */
32
+ warn?: (text: string) => void;
33
+ };
34
+ export declare function runJiraRead(argv: readonly string[], options?: JiraReadOptions): Promise<number>;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Reading Jira without the MCP channel.
3
+ *
4
+ * JAM's reads have lived only behind MCP tools. That is the right home when the
5
+ * agent's session already has them - but a session that registers JAM cannot be
6
+ * shown the new tools: Claude Code has no reload surface (`claude mcp` offers
7
+ * add / get / list / login / remove / reset-project-choices / serve, and nothing
8
+ * that re-reads registrations for a running session). So the agent that just
9
+ * installed JAM has to wait for its next session before it can read anything.
10
+ *
11
+ * What it did instead was worse: it answered Jira questions from whatever was at
12
+ * hand - git log, the code host, project documents - which is the exact
13
+ * substitution JAM exists to prevent.
14
+ *
15
+ * This is the same read, addressed differently. `search` / `context` / `full`
16
+ * call the same application functions the tools call, with the same deps, the
17
+ * same policies and the same `meta`. Nothing here re-implements a read, and
18
+ * nothing here is a second source of truth.
19
+ *
20
+ * Contract, enforced by tests:
21
+ * stdout - one JSON document and nothing else, no ANSI, no prompts
22
+ * stderr - diagnostics only
23
+ */
24
+ import { getFullIssueContext } from "../application/get-full-issue-context.js";
25
+ import { getIssueContext } from "../application/get-issue-context.js";
26
+ import { searchIssues } from "../application/search-issues.js";
27
+ import { buildDeps } from "../deps.js";
28
+ import { toJamError } from "../domain/errors.js";
29
+ export const JIRA_READ_USAGE = `Usage:
30
+ jam jira search <jql> [--scope preview|complete]
31
+ jam jira context <KEY> [KEY...]
32
+ jam jira full <KEY> [KEY...]
33
+
34
+ Reads only. Output is one JSON document on stdout - the same result the MCP
35
+ tools return, for a session that cannot see them yet.
36
+ `;
37
+ /** `--scope complete` / `--scope=complete`, and nothing invented when absent. */
38
+ function flagValue(argv, flag) {
39
+ const index = argv.indexOf(flag);
40
+ if (index >= 0)
41
+ return argv[index + 1];
42
+ const inline = argv.find((arg) => arg.startsWith(`${flag}=`));
43
+ return inline ? inline.slice(flag.length + 1) : undefined;
44
+ }
45
+ const positional = (argv) => {
46
+ const out = [];
47
+ for (let i = 0; i < argv.length; i += 1) {
48
+ const arg = argv[i];
49
+ if (arg === "--scope") {
50
+ i += 1;
51
+ continue;
52
+ }
53
+ if (arg.startsWith("--"))
54
+ continue;
55
+ out.push(arg);
56
+ }
57
+ return out;
58
+ };
59
+ export async function runJiraRead(argv, options = {}) {
60
+ const write = options.write ?? ((text) => process.stdout.write(text));
61
+ const warn = options.warn ?? ((text) => process.stderr.write(text));
62
+ const [subcommand, ...rest] = argv;
63
+ if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
64
+ warn(JIRA_READ_USAGE);
65
+ return subcommand ? 0 : 1;
66
+ }
67
+ if (subcommand !== "search" && subcommand !== "context" && subcommand !== "full") {
68
+ warn(`Unknown jira command: ${subcommand}\n\n${JIRA_READ_USAGE}`);
69
+ return 1;
70
+ }
71
+ const args = positional(rest);
72
+ if (args.length === 0) {
73
+ warn(subcommand === "search" ? "jam jira search needs a JQL query.\n" : `jam jira ${subcommand} needs at least one issue key.\n`);
74
+ return 1;
75
+ }
76
+ const scope = flagValue(rest, "--scope");
77
+ if (scope !== undefined && scope !== "preview" && scope !== "complete") {
78
+ warn(`--scope is preview|complete (got ${scope}).\n`);
79
+ return 1;
80
+ }
81
+ try {
82
+ const { deps: injected, write: _w, warn: _n, ...depsOptions } = options;
83
+ const deps = injected ?? (await buildDeps(depsOptions));
84
+ const result = subcommand === "search"
85
+ ? await searchIssues(deps, { jql: args.join(" "), ...(scope ? { scope } : {}) })
86
+ : subcommand === "context"
87
+ ? await getIssueContext(deps, { issueKeys: args })
88
+ : await getFullIssueContext(deps, { issueKeys: args });
89
+ write(`${JSON.stringify(result)}\n`);
90
+ return 0;
91
+ }
92
+ catch (err) {
93
+ // The same normalized codes the tools produce - an agent reads one contract,
94
+ // not two.
95
+ write(`${JSON.stringify(toJamError(err).toPayload())}\n`);
96
+ return 1;
97
+ }
98
+ }
@@ -3,5 +3,5 @@
3
3
  * points (notably @jam-mcp/bootstrap) can forward to exactly these commands
4
4
  * instead of reimplementing them.
5
5
  */
6
- export declare const USAGE = "jam - Jira Agent MCP\n\nUsage:\n jam serve Run the MCP server over stdio (default; this is what Claude Code / Codex launch)\n jam doctor Diagnose config, credentials and Jira connectivity\n jam setup [--project KEY] [--shared] [--migrate] [--non-interactive]\n Wire up this project and run doctor. Binds it to you\n alone, writing nothing to the repository; --shared\n adopts JAM for the team (project.yaml, .mcp.json)\n jam runtime Show which JAM build this machine runs\n jam runtime use package | development <path>\n Change it (writes ~/.jam/config.yaml only, never a project)\n jam auth login Store Jira credentials in this user's OS secret store\n jam auth logout Remove them again\n\nFor coding agents and scripts (stdout is JSON only, never prompts):\n jam setup --agent One shot: detect, plan, apply what is safe, verify\n jam setup plan --json Report what setup would change, changing nothing\n jam setup apply --non-interactive --json\n Execute the plan\n jam doctor --json Health check as structured output\n jam auth status --json Whether Jira credentials are configured (never their value)\n\nEnvironment:\n JIRA_BASE_URL https://your-site.atlassian.net\n JIRA_EMAIL Atlassian account email\n JIRA_API_TOKEN Atlassian API token\n JAM_PROJECT_KEY Jira project key, used by `jam setup`/`jam serve` when no\n .jira-agent/project.yaml exists yet\n\nCredentials and JAM_PROJECT_KEY are read from the current shell's environment\nfirst, then (on Windows) from the User environment - so a value set with\n`setx` works without opening a new terminal.\n";
6
+ export declare const USAGE = "jam - Jira Agent MCP\n\nUsage:\n jam serve Run the MCP server over stdio (default; this is what Claude Code / Codex launch)\n jam doctor Diagnose config, credentials and Jira connectivity\n jam setup [--project KEY] [--shared] [--migrate] [--non-interactive]\n Wire up this project and run doctor. Binds it to you\n alone, writing nothing to the repository; --shared\n adopts JAM for the team (project.yaml, .mcp.json)\n jam runtime Show which JAM build this machine runs\n jam runtime use package | development <path>\n Change it (writes ~/.jam/config.yaml only, never a project)\n jam auth login Store Jira credentials in this user's OS secret store\n jam auth logout Remove them again\n\nFor coding agents and scripts (stdout is JSON only, never prompts):\n jam setup --agent One shot: detect, plan, apply what is safe, verify\n jam setup plan --json Report what setup would change, changing nothing\n jam setup apply --non-interactive --json\n Execute the plan\n jam doctor --json Health check as structured output\n jam auth status --json Whether Jira credentials are configured (never their value)\n jam jira search <jql> [--scope preview|complete]\n jam jira context <KEY> [KEY...]\n jam jira full <KEY> [KEY...]\n Read Jira from the shell - the same reads the MCP\n tools do, for a session that cannot see them yet\n\nEnvironment:\n JIRA_BASE_URL https://your-site.atlassian.net\n JIRA_EMAIL Atlassian account email\n JIRA_API_TOKEN Atlassian API token\n JAM_PROJECT_KEY Jira project key, used by `jam setup`/`jam serve` when no\n .jira-agent/project.yaml exists yet\n\nCredentials and JAM_PROJECT_KEY are read from the current shell's environment\nfirst, then (on Windows) from the User environment - so a value set with\n`setx` works without opening a new terminal.\n";
7
7
  export declare function runJamCommand(argv: string[]): Promise<number>;
package/dist/cli-entry.js CHANGED
@@ -6,6 +6,7 @@ import { setup } from "./cli/setup.js";
6
6
  import { runSetupWizard } from "./cli/setup-wizard.js";
7
7
  import { reportPromptError, Ui } from "./cli/ui.js";
8
8
  import { authStatusCommand, doctorJsonCommand, setupAgentCommand, setupApplyCommand, setupPlanCommand, } from "./cli/agent-api.js";
9
+ import { runJiraRead } from "./cli/jira-read.js";
9
10
  /**
10
11
  * Command dispatch for the JAM CLI, separated from the bin so other entry
11
12
  * points (notably @jam-mcp/bootstrap) can forward to exactly these commands
@@ -33,6 +34,11 @@ For coding agents and scripts (stdout is JSON only, never prompts):
33
34
  Execute the plan
34
35
  jam doctor --json Health check as structured output
35
36
  jam auth status --json Whether Jira credentials are configured (never their value)
37
+ jam jira search <jql> [--scope preview|complete]
38
+ jam jira context <KEY> [KEY...]
39
+ jam jira full <KEY> [KEY...]
40
+ Read Jira from the shell - the same reads the MCP
41
+ tools do, for a session that cannot see them yet
36
42
 
37
43
  Environment:
38
44
  JIRA_BASE_URL https://your-site.atlassian.net
@@ -112,6 +118,10 @@ export async function runJamCommand(argv) {
112
118
  process.stderr.write("Usage: jam auth login | status [--json] | logout\n");
113
119
  return 1;
114
120
  }
121
+ case "jira":
122
+ // Reads addressed to the shell, for a session that cannot see the MCP
123
+ // tools yet. Same application path as the tools - see cli/jira-read.ts.
124
+ return runJiraRead(rest);
115
125
  case "help":
116
126
  case "--help":
117
127
  case "-h":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jam-mcp/server",
3
- "version": "1.4.5",
3
+ "version": "1.4.6",
4
4
  "description": "JAM (Jira Agent MCP) - agent-facing Jira access layer: MCP server, setup core, and CLI",
5
5
  "keywords": [
6
6
  "jira",
@@ -41,7 +41,7 @@
41
41
  "test:watch": "vitest"
42
42
  },
43
43
  "dependencies": {
44
- "@jam-mcp/launcher": "1.4.5",
44
+ "@jam-mcp/launcher": "1.4.6",
45
45
  "@modelcontextprotocol/sdk": "^1.30.0",
46
46
  "yaml": "^2.9.0",
47
47
  "zod": "^4.4.3"