@1medium/cli 1.3.0 → 1.4.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/package.json +1 -1
- package/src/api.js +4 -4
- package/src/mcp-server.js +109 -23
- package/src/session-state.js +105 -0
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const fetch = require("node-fetch");
|
|
4
|
-
const
|
|
4
|
+
const sessionState = require("./session-state");
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Get API base URL
|
|
8
8
|
*/
|
|
9
9
|
function getApiUrl() {
|
|
10
|
-
return
|
|
10
|
+
return sessionState.get("apiUrl") || "https://1medium.ai/api";
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Get auth token
|
|
15
15
|
*/
|
|
16
16
|
function getToken() {
|
|
17
|
-
const token =
|
|
17
|
+
const token = sessionState.get("token");
|
|
18
18
|
if (!token) {
|
|
19
19
|
throw new Error(
|
|
20
20
|
"No token configured. Run '1m login' to set your API token."
|
|
@@ -27,7 +27,7 @@ function getToken() {
|
|
|
27
27
|
* Get configured org ID (if any)
|
|
28
28
|
*/
|
|
29
29
|
function getOrgId() {
|
|
30
|
-
return
|
|
30
|
+
return sessionState.get("orgId") || null;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
package/src/mcp-server.js
CHANGED
|
@@ -8,7 +8,7 @@ const {
|
|
|
8
8
|
ListToolsRequestSchema,
|
|
9
9
|
} = require("@modelcontextprotocol/sdk/types.js");
|
|
10
10
|
const api = require("./api");
|
|
11
|
-
const
|
|
11
|
+
const sessionState = require("./session-state");
|
|
12
12
|
|
|
13
13
|
const server = new Server(
|
|
14
14
|
{
|
|
@@ -97,7 +97,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
name: "task_list",
|
|
100
|
-
description: "List tasks from 1Medium. Returns open tasks by default.",
|
|
100
|
+
description: "List tasks from 1Medium. Returns open tasks by default. Filters by active project unless project_id is explicitly set or all_projects is true.",
|
|
101
101
|
inputSchema: {
|
|
102
102
|
type: "object",
|
|
103
103
|
properties: {
|
|
@@ -115,6 +115,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
115
115
|
type: "number",
|
|
116
116
|
description: "Maximum number of tasks to return",
|
|
117
117
|
},
|
|
118
|
+
project_id: {
|
|
119
|
+
type: "string",
|
|
120
|
+
description: "Filter by specific project ID. Overrides active project.",
|
|
121
|
+
},
|
|
122
|
+
all_projects: {
|
|
123
|
+
type: "boolean",
|
|
124
|
+
description: "Set to true to list tasks from all projects, ignoring active project filter.",
|
|
125
|
+
},
|
|
118
126
|
},
|
|
119
127
|
},
|
|
120
128
|
},
|
|
@@ -368,6 +376,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
368
376
|
required: ["id"],
|
|
369
377
|
},
|
|
370
378
|
},
|
|
379
|
+
{
|
|
380
|
+
name: "config_persist",
|
|
381
|
+
description: "Persist current session settings (org/space/project) to the config file. Use this when you want changes to apply to all Claude Code tabs and persist across restarts.",
|
|
382
|
+
inputSchema: {
|
|
383
|
+
type: "object",
|
|
384
|
+
properties: {},
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
name: "config_reset",
|
|
389
|
+
description: "Reset session to values from the config file. Use this to discard any org/space/project changes made in this session and reload from persistent config.",
|
|
390
|
+
inputSchema: {
|
|
391
|
+
type: "object",
|
|
392
|
+
properties: {},
|
|
393
|
+
},
|
|
394
|
+
},
|
|
371
395
|
],
|
|
372
396
|
};
|
|
373
397
|
});
|
|
@@ -377,7 +401,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
377
401
|
const { name, arguments: args } = request.params;
|
|
378
402
|
|
|
379
403
|
// Check if configured
|
|
380
|
-
const token =
|
|
404
|
+
const token = sessionState.get("token");
|
|
381
405
|
if (!token) {
|
|
382
406
|
return {
|
|
383
407
|
content: [
|
|
@@ -397,7 +421,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
397
421
|
case "org_list": {
|
|
398
422
|
result = await api.listOrgs();
|
|
399
423
|
const orgs = result.orgs || [];
|
|
400
|
-
const currentOrgId =
|
|
424
|
+
const currentOrgId = sessionState.get("orgId");
|
|
401
425
|
|
|
402
426
|
if (orgs.length === 0) {
|
|
403
427
|
return {
|
|
@@ -424,9 +448,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
424
448
|
}
|
|
425
449
|
|
|
426
450
|
case "org_set": {
|
|
427
|
-
|
|
451
|
+
sessionState.set("orgId", args.id);
|
|
428
452
|
if (args.name) {
|
|
429
|
-
|
|
453
|
+
sessionState.set("orgName", args.name);
|
|
430
454
|
}
|
|
431
455
|
return {
|
|
432
456
|
content: [
|
|
@@ -439,8 +463,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
439
463
|
}
|
|
440
464
|
|
|
441
465
|
case "org_get": {
|
|
442
|
-
const orgId =
|
|
443
|
-
const orgName =
|
|
466
|
+
const orgId = sessionState.get("orgId");
|
|
467
|
+
const orgName = sessionState.get("orgName");
|
|
444
468
|
|
|
445
469
|
if (!orgId) {
|
|
446
470
|
return {
|
|
@@ -460,7 +484,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
460
484
|
|
|
461
485
|
case "task_create": {
|
|
462
486
|
// Use provided project_id or fall back to configured default
|
|
463
|
-
const projectId = args.project_id ||
|
|
487
|
+
const projectId = args.project_id || sessionState.get("projectId") || null;
|
|
464
488
|
const payload = {
|
|
465
489
|
title: args.title,
|
|
466
490
|
body_md: args.body,
|
|
@@ -490,18 +514,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
490
514
|
if (args.priority) params.priority = args.priority;
|
|
491
515
|
if (args.limit) params.limit = args.limit;
|
|
492
516
|
|
|
517
|
+
// Filter by project: explicit arg > active project (unless all_projects is true)
|
|
518
|
+
if (args.project_id) {
|
|
519
|
+
params.project_id = args.project_id;
|
|
520
|
+
} else if (!args.all_projects) {
|
|
521
|
+
const activeProjectId = sessionState.get("projectId");
|
|
522
|
+
if (activeProjectId) {
|
|
523
|
+
params.project_id = activeProjectId;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
493
527
|
result = await api.listTasks(params);
|
|
494
528
|
const tasks = result.tasks || [];
|
|
495
529
|
|
|
530
|
+
// Build header with filter info
|
|
531
|
+
const activeProjectName = sessionState.get("projectName");
|
|
532
|
+
const filterInfo = params.project_id
|
|
533
|
+
? ` in project "${activeProjectName || params.project_id}"`
|
|
534
|
+
: " (all projects)";
|
|
535
|
+
|
|
496
536
|
if (tasks.length === 0) {
|
|
497
537
|
return {
|
|
498
|
-
content: [{ type: "text", text:
|
|
538
|
+
content: [{ type: "text", text: `No tasks found${filterInfo}.` }],
|
|
499
539
|
};
|
|
500
540
|
}
|
|
501
541
|
|
|
502
542
|
const taskList = tasks
|
|
503
543
|
.map((t) => {
|
|
504
|
-
const check = t.
|
|
544
|
+
const check = t.completed_at ? "[x]" : "[ ]";
|
|
505
545
|
return `${check} ${t.priority} ${t.title}\n ID: ${t.id}`;
|
|
506
546
|
})
|
|
507
547
|
.join("\n\n");
|
|
@@ -510,7 +550,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
510
550
|
content: [
|
|
511
551
|
{
|
|
512
552
|
type: "text",
|
|
513
|
-
text: `Tasks (${result.total} total):\n\n${taskList}`,
|
|
553
|
+
text: `Tasks${filterInfo} (${result.total} total):\n\n${taskList}`,
|
|
514
554
|
},
|
|
515
555
|
],
|
|
516
556
|
};
|
|
@@ -519,7 +559,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
519
559
|
case "task_get": {
|
|
520
560
|
result = await api.getTask(args.id);
|
|
521
561
|
const t = result.task;
|
|
522
|
-
const status = t.
|
|
562
|
+
const status = t.completed_at ? "completed" : "open";
|
|
523
563
|
|
|
524
564
|
let text = `${t.title}\n\n ID: ${t.id}\n Status: ${status}\n Priority: ${t.priority}`;
|
|
525
565
|
|
|
@@ -586,7 +626,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
586
626
|
case "space_list": {
|
|
587
627
|
result = await api.listSpaces();
|
|
588
628
|
const spaces = result.spaces || [];
|
|
589
|
-
const currentSpaceId =
|
|
629
|
+
const currentSpaceId = sessionState.get("spaceId");
|
|
590
630
|
|
|
591
631
|
if (spaces.length === 0) {
|
|
592
632
|
return {
|
|
@@ -612,9 +652,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
612
652
|
}
|
|
613
653
|
|
|
614
654
|
case "space_set": {
|
|
615
|
-
|
|
655
|
+
sessionState.set("spaceId", args.id);
|
|
616
656
|
if (args.name) {
|
|
617
|
-
|
|
657
|
+
sessionState.set("spaceName", args.name);
|
|
618
658
|
}
|
|
619
659
|
return {
|
|
620
660
|
content: [
|
|
@@ -627,8 +667,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
627
667
|
}
|
|
628
668
|
|
|
629
669
|
case "space_get": {
|
|
630
|
-
const spaceId =
|
|
631
|
-
const spaceName =
|
|
670
|
+
const spaceId = sessionState.get("spaceId");
|
|
671
|
+
const spaceName = sessionState.get("spaceName");
|
|
632
672
|
|
|
633
673
|
if (!spaceId) {
|
|
634
674
|
return {
|
|
@@ -652,7 +692,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
652
692
|
|
|
653
693
|
result = await api.listProjects(params);
|
|
654
694
|
const projects = result.projects || [];
|
|
655
|
-
const currentProjectId =
|
|
695
|
+
const currentProjectId = sessionState.get("projectId");
|
|
656
696
|
|
|
657
697
|
if (projects.length === 0) {
|
|
658
698
|
return {
|
|
@@ -682,9 +722,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
682
722
|
}
|
|
683
723
|
|
|
684
724
|
case "project_set": {
|
|
685
|
-
|
|
725
|
+
sessionState.set("projectId", args.id);
|
|
686
726
|
if (args.name) {
|
|
687
|
-
|
|
727
|
+
sessionState.set("projectName", args.name);
|
|
688
728
|
}
|
|
689
729
|
return {
|
|
690
730
|
content: [
|
|
@@ -697,8 +737,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
697
737
|
}
|
|
698
738
|
|
|
699
739
|
case "project_get": {
|
|
700
|
-
const projectId =
|
|
701
|
-
const projectName =
|
|
740
|
+
const projectId = sessionState.get("projectId");
|
|
741
|
+
const projectName = sessionState.get("projectName");
|
|
702
742
|
|
|
703
743
|
if (!projectId) {
|
|
704
744
|
return {
|
|
@@ -803,6 +843,52 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
803
843
|
};
|
|
804
844
|
}
|
|
805
845
|
|
|
846
|
+
case "config_persist": {
|
|
847
|
+
sessionState.persistSession();
|
|
848
|
+
const state = sessionState.getSessionState();
|
|
849
|
+
let text = "Session settings persisted to config file.";
|
|
850
|
+
if (state.orgName || state.orgId) {
|
|
851
|
+
text += `\n Org: ${state.orgName || state.orgId || "(none)"}`;
|
|
852
|
+
}
|
|
853
|
+
if (state.spaceName || state.spaceId) {
|
|
854
|
+
text += `\n Space: ${state.spaceName || state.spaceId || "(none)"}`;
|
|
855
|
+
}
|
|
856
|
+
if (state.projectName || state.projectId) {
|
|
857
|
+
text += `\n Project: ${state.projectName || state.projectId || "(none)"}`;
|
|
858
|
+
}
|
|
859
|
+
return {
|
|
860
|
+
content: [
|
|
861
|
+
{
|
|
862
|
+
type: "text",
|
|
863
|
+
text,
|
|
864
|
+
},
|
|
865
|
+
],
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
case "config_reset": {
|
|
870
|
+
sessionState.resetSession();
|
|
871
|
+
const state = sessionState.getSessionState();
|
|
872
|
+
let text = "Session reset from config file.";
|
|
873
|
+
if (state.orgName || state.orgId) {
|
|
874
|
+
text += `\n Org: ${state.orgName || state.orgId || "(none)"}`;
|
|
875
|
+
}
|
|
876
|
+
if (state.spaceName || state.spaceId) {
|
|
877
|
+
text += `\n Space: ${state.spaceName || state.spaceId || "(none)"}`;
|
|
878
|
+
}
|
|
879
|
+
if (state.projectName || state.projectId) {
|
|
880
|
+
text += `\n Project: ${state.projectName || state.projectId || "(none)"}`;
|
|
881
|
+
}
|
|
882
|
+
return {
|
|
883
|
+
content: [
|
|
884
|
+
{
|
|
885
|
+
type: "text",
|
|
886
|
+
text,
|
|
887
|
+
},
|
|
888
|
+
],
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
|
|
806
892
|
default:
|
|
807
893
|
return {
|
|
808
894
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Session-scoped state for MCP server
|
|
5
|
+
*
|
|
6
|
+
* This module wraps the persistent config with an in-memory layer for session-scoped keys.
|
|
7
|
+
* Multiple Claude Code tabs can run separate MCP server processes without affecting each other's
|
|
8
|
+
* org/space/project selections.
|
|
9
|
+
*
|
|
10
|
+
* Session-scoped keys (in-memory only):
|
|
11
|
+
* - orgId, orgName, spaceId, spaceName, projectId, projectName
|
|
12
|
+
*
|
|
13
|
+
* Persistent keys (always read/write from config file):
|
|
14
|
+
* - token, apiUrl
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const config = require("./config");
|
|
18
|
+
|
|
19
|
+
// Keys that are session-scoped (stored in memory, not persisted by default)
|
|
20
|
+
const SESSION_KEYS = [
|
|
21
|
+
"orgId",
|
|
22
|
+
"orgName",
|
|
23
|
+
"spaceId",
|
|
24
|
+
"spaceName",
|
|
25
|
+
"projectId",
|
|
26
|
+
"projectName",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
// In-memory session state, initialized from config
|
|
30
|
+
const sessionState = {};
|
|
31
|
+
|
|
32
|
+
// Initialize session state from config on module load
|
|
33
|
+
for (const key of SESSION_KEYS) {
|
|
34
|
+
sessionState[key] = config.get(key);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get a config value
|
|
39
|
+
* Session-scoped keys return from memory; others fall through to persistent config
|
|
40
|
+
*/
|
|
41
|
+
function get(key) {
|
|
42
|
+
if (SESSION_KEYS.includes(key)) {
|
|
43
|
+
return sessionState[key];
|
|
44
|
+
}
|
|
45
|
+
return config.get(key);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Set a config value
|
|
50
|
+
* Session-scoped keys are stored in memory only; others persist to config file
|
|
51
|
+
*/
|
|
52
|
+
function set(key, value) {
|
|
53
|
+
if (SESSION_KEYS.includes(key)) {
|
|
54
|
+
sessionState[key] = value;
|
|
55
|
+
} else {
|
|
56
|
+
config.set(key, value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Persist current session state to the config file
|
|
62
|
+
* Call this to make session changes permanent across all Claude Code tabs
|
|
63
|
+
*/
|
|
64
|
+
function persistSession() {
|
|
65
|
+
for (const key of SESSION_KEYS) {
|
|
66
|
+
config.set(key, sessionState[key]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Reset session state from the config file
|
|
72
|
+
* Call this to discard session changes and reload from persistent config
|
|
73
|
+
*/
|
|
74
|
+
function resetSession() {
|
|
75
|
+
for (const key of SESSION_KEYS) {
|
|
76
|
+
sessionState[key] = config.get(key);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get the list of session-scoped keys
|
|
82
|
+
*/
|
|
83
|
+
function getSessionKeys() {
|
|
84
|
+
return [...SESSION_KEYS];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get all current session values (for debugging/display)
|
|
89
|
+
*/
|
|
90
|
+
function getSessionState() {
|
|
91
|
+
const state = {};
|
|
92
|
+
for (const key of SESSION_KEYS) {
|
|
93
|
+
state[key] = sessionState[key];
|
|
94
|
+
}
|
|
95
|
+
return state;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
get,
|
|
100
|
+
set,
|
|
101
|
+
persistSession,
|
|
102
|
+
resetSession,
|
|
103
|
+
getSessionKeys,
|
|
104
|
+
getSessionState,
|
|
105
|
+
};
|