@jam-mcp/server 1.1.0 → 1.2.0
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 +2 -2
- package/dist/adapters/credentials/windows-user-env.d.ts +3 -1
- package/dist/adapters/credentials/windows-user-env.js +20 -1
- package/dist/adapters/jira-cloud/jira-create-metadata.adapter.d.ts +27 -0
- package/dist/adapters/jira-cloud/jira-create-metadata.adapter.js +81 -0
- package/dist/adapters/jira-cloud/jira-read.adapter.d.ts +10 -1
- package/dist/adapters/jira-cloud/jira-read.adapter.js +17 -0
- package/dist/adapters/jira-cloud/jira-write.adapter.d.ts +13 -7
- package/dist/adapters/jira-cloud/jira-write.adapter.js +25 -20
- package/dist/application/apply-create-issue.d.ts +20 -0
- package/dist/application/apply-create-issue.js +187 -0
- package/dist/application/apply-write.js +18 -3
- package/dist/application/plan-create-issue.d.ts +44 -0
- package/dist/application/plan-create-issue.js +188 -0
- package/dist/application/plan-write.d.ts +10 -3
- package/dist/application/plan-write.js +54 -11
- package/dist/application/write-plan-store.d.ts +2 -2
- package/dist/application/write-plan-store.js +13 -1
- package/dist/bootstrap/mcp-config-merger.d.ts +1 -1
- package/dist/cli/auth.d.ts +6 -0
- package/dist/cli/auth.js +2 -1
- package/dist/deps.d.ts +11 -0
- package/dist/deps.js +6 -0
- package/dist/domain/adf.d.ts +35 -0
- package/dist/domain/adf.js +65 -0
- package/dist/domain/errors.d.ts +1 -1
- package/dist/domain/errors.js +9 -0
- package/dist/domain/write.d.ts +128 -15
- package/dist/domain/write.js +33 -1
- package/dist/mcp/create-server.d.ts +4 -0
- package/dist/mcp/create-server.js +5 -0
- package/dist/mcp/tools/jira-write-plan.tool.js +34 -6
- package/dist/policy/consistency-policy.d.ts +10 -4
- package/dist/policy/create-policy.d.ts +86 -0
- package/dist/policy/create-policy.js +182 -0
- package/dist/policy/write-policy.d.ts +10 -1
- package/dist/policy/write-policy.js +15 -1
- package/dist/ports/jira-create-metadata.port.d.ts +25 -0
- package/dist/ports/jira-create-metadata.port.js +1 -0
- package/dist/ports/jira-read.port.d.ts +23 -0
- package/dist/ports/jira-write.port.d.ts +9 -0
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FieldUpdateInput, type JiraTransition, type WriteOperation } from "../domain/write.js";
|
|
1
|
+
import { type ExistingIssueOperation, type FieldUpdateInput, type JiraTransition, type WriteOperation } from "../domain/write.js";
|
|
2
2
|
/**
|
|
3
3
|
* What a write is allowed to be, decided before anything reaches Jira.
|
|
4
4
|
*
|
|
@@ -32,6 +32,15 @@ export declare function projectKeyOf(issueKey: string): string | undefined;
|
|
|
32
32
|
*/
|
|
33
33
|
export declare function assertWriteScope(issueKey: string, configuredProject: string): string;
|
|
34
34
|
export declare function assertOperationAllowed(operation: string): WriteOperation;
|
|
35
|
+
/**
|
|
36
|
+
* Narrow an already-allowed operation to one that acts on an existing issue.
|
|
37
|
+
*
|
|
38
|
+
* Reached only after routing has sent `issue.create` elsewhere, so this is a
|
|
39
|
+
* type-level guarantee rather than a second policy decision - but it is a
|
|
40
|
+
* guarantee worth having, because everything downstream reads an issue key
|
|
41
|
+
* that creation does not have.
|
|
42
|
+
*/
|
|
43
|
+
export declare function assertExistingIssueOperation(operation: string): ExistingIssueOperation;
|
|
35
44
|
/**
|
|
36
45
|
* Reject anything outside the field whitelist before planning goes further.
|
|
37
46
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JamError } from "../domain/errors.js";
|
|
2
|
-
import { isWritableField, isWriteOperation, WRITABLE_FIELDS, WRITE_OPERATIONS, } from "../domain/write.js";
|
|
2
|
+
import { EXISTING_ISSUE_OPERATIONS, isExistingIssueOperation, isWritableField, isWriteOperation, WRITABLE_FIELDS, WRITE_OPERATIONS, } from "../domain/write.js";
|
|
3
3
|
/**
|
|
4
4
|
* What a write is allowed to be, decided before anything reaches Jira.
|
|
5
5
|
*
|
|
@@ -54,6 +54,20 @@ export function assertOperationAllowed(operation) {
|
|
|
54
54
|
}
|
|
55
55
|
return operation;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Narrow an already-allowed operation to one that acts on an existing issue.
|
|
59
|
+
*
|
|
60
|
+
* Reached only after routing has sent `issue.create` elsewhere, so this is a
|
|
61
|
+
* type-level guarantee rather than a second policy decision - but it is a
|
|
62
|
+
* guarantee worth having, because everything downstream reads an issue key
|
|
63
|
+
* that creation does not have.
|
|
64
|
+
*/
|
|
65
|
+
export function assertExistingIssueOperation(operation) {
|
|
66
|
+
if (!isExistingIssueOperation(operation)) {
|
|
67
|
+
throw new JamError("JAM_WRITE_OPERATION_NOT_ALLOWED", `"${operation}" does not change an existing issue. Operations that do: ${EXISTING_ISSUE_OPERATIONS.join(", ")}.`, { operation, supported: [...EXISTING_ISSUE_OPERATIONS] });
|
|
68
|
+
}
|
|
69
|
+
return operation;
|
|
70
|
+
}
|
|
57
71
|
/**
|
|
58
72
|
* Reject anything outside the field whitelist before planning goes further.
|
|
59
73
|
*
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CreateFieldMetadata, CreateIssueType } from "../domain/write.js";
|
|
2
|
+
/**
|
|
3
|
+
* What Jira will accept when creating an issue in one project, right now.
|
|
4
|
+
*
|
|
5
|
+
* Separate from `JiraWritePort` on purpose. Everything behind that port
|
|
6
|
+
* changes something; nothing behind this one does. Folding schema discovery
|
|
7
|
+
* into the write port would put a read on the far side of a boundary whose
|
|
8
|
+
* whole documented rule is "these calls mutate, and none of them retry" - and
|
|
9
|
+
* the rule is worth more than the one fewer interface.
|
|
10
|
+
*
|
|
11
|
+
* Kept out of `JiraReadPort` too, for the opposite reason: that port answers
|
|
12
|
+
* questions about issues, with completeness semantics attached to every
|
|
13
|
+
* result. This answers a question about a project's configuration, and
|
|
14
|
+
* `meta.complete` would mean nothing here.
|
|
15
|
+
*
|
|
16
|
+
* These calls do not retry. Their answers decide a mutation, and a retried
|
|
17
|
+
* answer is a possibly-stale one - the same argument that keeps
|
|
18
|
+
* `getTransitions` on the non-retrying side.
|
|
19
|
+
*/
|
|
20
|
+
export interface JiraCreateMetadataPort {
|
|
21
|
+
/** Issue types this account may create in this project. */
|
|
22
|
+
getIssueTypes(projectKey: string): Promise<CreateIssueType[]>;
|
|
23
|
+
/** The create screen's fields for one issue type in one project. */
|
|
24
|
+
getCreateFields(projectKey: string, issueTypeId: string): Promise<CreateFieldMetadata[]>;
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -20,6 +20,15 @@ export type GetIssuesRequest = {
|
|
|
20
20
|
keys: string[];
|
|
21
21
|
fields: string[];
|
|
22
22
|
};
|
|
23
|
+
export type GetIssueRequest = {
|
|
24
|
+
key: string;
|
|
25
|
+
fields: string[];
|
|
26
|
+
};
|
|
27
|
+
export type GetIssueResult = {
|
|
28
|
+
/** Absent when Jira has no such issue, or this account cannot see it. */
|
|
29
|
+
issue?: FullIssueContext;
|
|
30
|
+
responseBytes: number;
|
|
31
|
+
};
|
|
23
32
|
export type GetIssuesResult = {
|
|
24
33
|
issues: FullIssueContext[];
|
|
25
34
|
/** Keys that were requested but not returned (missing or not permitted). */
|
|
@@ -59,6 +68,20 @@ export type ListProjectsResult = {
|
|
|
59
68
|
};
|
|
60
69
|
export interface JiraReadPort {
|
|
61
70
|
searchPage(req: SearchPageRequest): Promise<SearchPageResult>;
|
|
71
|
+
/**
|
|
72
|
+
* One issue, read directly by key.
|
|
73
|
+
*
|
|
74
|
+
* Separate from `getIssues` because ConsistencyPolicy requires a direct
|
|
75
|
+
* issue GET for anything that decides or confirms a write, and `getIssues`
|
|
76
|
+
* is not one: it is a bulk endpoint that takes a list, and a bulk read is
|
|
77
|
+
* free to answer from a different path than a single-issue GET. The
|
|
78
|
+
* distinction only matters in one place - the write plane - which is exactly
|
|
79
|
+
* where being wrong about it is most expensive.
|
|
80
|
+
*
|
|
81
|
+
* Reads and writes both use it: the pre-write conflict check, the post-write
|
|
82
|
+
* confirmation, and the post-create confirmation.
|
|
83
|
+
*/
|
|
84
|
+
getIssue(req: GetIssueRequest): Promise<GetIssueResult>;
|
|
62
85
|
getIssues(req: GetIssuesRequest): Promise<GetIssuesResult>;
|
|
63
86
|
getComments(req: GetCommentsRequest): Promise<GetCommentsResult>;
|
|
64
87
|
/** Used by `jam doctor` to prove authentication works. */
|
|
@@ -16,6 +16,15 @@ import type { JiraTransition } from "../domain/write.js";
|
|
|
16
16
|
* surface is not: only whitelisted operations reach it (see domain/write.ts).
|
|
17
17
|
*/
|
|
18
18
|
export interface JiraWritePort {
|
|
19
|
+
/**
|
|
20
|
+
* Create one issue. The only call here that brings an issue into existence,
|
|
21
|
+
* and the one where a retry is most expensive: a duplicate update is a
|
|
22
|
+
* no-op, a duplicate create is a second issue on someone's board.
|
|
23
|
+
*/
|
|
24
|
+
createIssue(fields: Record<string, unknown>): Promise<{
|
|
25
|
+
id: string;
|
|
26
|
+
key: string;
|
|
27
|
+
}>;
|
|
19
28
|
updateIssue(key: string, fields: Record<string, unknown>): Promise<void>;
|
|
20
29
|
addComment(key: string, body: string): Promise<{
|
|
21
30
|
id: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jam-mcp/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
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.
|
|
44
|
+
"@jam-mcp/launcher": "1.2.0",
|
|
45
45
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
46
46
|
"yaml": "^2.9.0",
|
|
47
47
|
"zod": "^4.4.3"
|