memtask 0.0.1__tar.gz
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.
- memtask-0.0.1/MANIFEST.in +2 -0
- memtask-0.0.1/PKG-INFO +104 -0
- memtask-0.0.1/README.md +94 -0
- memtask-0.0.1/docs/assets/memtask-logo.png +0 -0
- memtask-0.0.1/pyproject.toml +28 -0
- memtask-0.0.1/setup.cfg +4 -0
- memtask-0.0.1/src/memtask/__init__.py +5 -0
- memtask-0.0.1/src/memtask/__main__.py +5 -0
- memtask-0.0.1/src/memtask/api.py +328 -0
- memtask-0.0.1/src/memtask/app.py +73 -0
- memtask-0.0.1/src/memtask/cli.py +303 -0
- memtask-0.0.1/src/memtask/manager.py +744 -0
- memtask-0.0.1/src/memtask/storage.py +150 -0
- memtask-0.0.1/src/memtask.egg-info/PKG-INFO +104 -0
- memtask-0.0.1/src/memtask.egg-info/SOURCES.txt +21 -0
- memtask-0.0.1/src/memtask.egg-info/dependency_links.txt +1 -0
- memtask-0.0.1/src/memtask.egg-info/entry_points.txt +2 -0
- memtask-0.0.1/src/memtask.egg-info/requires.txt +4 -0
- memtask-0.0.1/src/memtask.egg-info/top_level.txt +1 -0
- memtask-0.0.1/tests/test_cli.py +92 -0
- memtask-0.0.1/tests/test_memories.py +67 -0
- memtask-0.0.1/tests/test_server_contract.py +86 -0
- memtask-0.0.1/tests/test_tasks.py +62 -0
memtask-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: memtask
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A local MCP server for durable agent task state and lightweight memory.
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: mcp
|
|
8
|
+
Provides-Extra: test
|
|
9
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<img src="docs/assets/memtask-logo.png" alt="MemTask logo" width="220">
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<h1 align="center">MemTask</h1>
|
|
16
|
+
|
|
17
|
+
MemTask is a local MCP server for developer-built agents that need durable task state and lightweight memory. It combines task planning, dependency tracking, active work selection, completion history, and scoped memories in a small SQLite-backed service. The goal is to give agents a structured place to manage agency: what to do next, what depends on what, and what context should persist across sessions.
|
|
18
|
+
|
|
19
|
+
## What It Provides
|
|
20
|
+
|
|
21
|
+
MemTask exposes two local primitives over MCP:
|
|
22
|
+
|
|
23
|
+
- Tasks: pending work, stable task references, parent/child dependency edges, active task selection, and completed task state.
|
|
24
|
+
- Memories: scoped pieces of context with confidence scores, optional parent memory relationships, tags, and task-memory references.
|
|
25
|
+
|
|
26
|
+
The server is intentionally local-first. State lives in SQLite, the tool surface is small, and the manager layer can be tested directly without running an MCP transport.
|
|
27
|
+
|
|
28
|
+
## Quickstart
|
|
29
|
+
|
|
30
|
+
Install MemTask:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install MemTask
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then ask MemTask for the MCP config to add to your agent:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
memtask install-help
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Most MCP clients should launch MemTask over stdio:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
memtask start --transport stdio
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If your client connects to a running HTTP server instead, start it in the background:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
memtask start --transport http --host 127.0.0.1 --port 8000
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
For local development from this repo:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
PYTHONPATH=src python -m memtask start --transport stdio
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Useful HTTP commands: `memtask status` and `memtask stop`.
|
|
61
|
+
|
|
62
|
+
## Storage
|
|
63
|
+
|
|
64
|
+
Runtime state in this repo is stored in `data/tasks.sqlite`.
|
|
65
|
+
|
|
66
|
+
When installed outside this repo, MemTask uses `~/.memtask/tasks.sqlite` by default. Set `MEMTASK_DB_PATH` to choose a specific SQLite path.
|
|
67
|
+
|
|
68
|
+
The server creates the required SQLite tables on startup using `CREATE TABLE IF NOT EXISTS`. There is no migration framework.
|
|
69
|
+
|
|
70
|
+
## Tools
|
|
71
|
+
|
|
72
|
+
Task tools:
|
|
73
|
+
|
|
74
|
+
- `list_tasks`
|
|
75
|
+
- `get_task`
|
|
76
|
+
- `add_task`
|
|
77
|
+
- `add_batch_tasks`
|
|
78
|
+
- `complete_task`
|
|
79
|
+
- `remove_task`
|
|
80
|
+
- `remove_all_tasks`
|
|
81
|
+
- `current_tasks`
|
|
82
|
+
- `set_current_task`
|
|
83
|
+
|
|
84
|
+
Memory tools:
|
|
85
|
+
|
|
86
|
+
- `remember`
|
|
87
|
+
- `recall`
|
|
88
|
+
- `get_memory`
|
|
89
|
+
- `update_memory`
|
|
90
|
+
- `delete_memory`
|
|
91
|
+
|
|
92
|
+
## Development
|
|
93
|
+
|
|
94
|
+
Run tests:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
python -m pytest
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Compile-check the package:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
python -m py_compile src/memtask/*.py tests/*.py
|
|
104
|
+
```
|
memtask-0.0.1/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/memtask-logo.png" alt="MemTask logo" width="220">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">MemTask</h1>
|
|
6
|
+
|
|
7
|
+
MemTask is a local MCP server for developer-built agents that need durable task state and lightweight memory. It combines task planning, dependency tracking, active work selection, completion history, and scoped memories in a small SQLite-backed service. The goal is to give agents a structured place to manage agency: what to do next, what depends on what, and what context should persist across sessions.
|
|
8
|
+
|
|
9
|
+
## What It Provides
|
|
10
|
+
|
|
11
|
+
MemTask exposes two local primitives over MCP:
|
|
12
|
+
|
|
13
|
+
- Tasks: pending work, stable task references, parent/child dependency edges, active task selection, and completed task state.
|
|
14
|
+
- Memories: scoped pieces of context with confidence scores, optional parent memory relationships, tags, and task-memory references.
|
|
15
|
+
|
|
16
|
+
The server is intentionally local-first. State lives in SQLite, the tool surface is small, and the manager layer can be tested directly without running an MCP transport.
|
|
17
|
+
|
|
18
|
+
## Quickstart
|
|
19
|
+
|
|
20
|
+
Install MemTask:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install MemTask
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Then ask MemTask for the MCP config to add to your agent:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
memtask install-help
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Most MCP clients should launch MemTask over stdio:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
memtask start --transport stdio
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If your client connects to a running HTTP server instead, start it in the background:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
memtask start --transport http --host 127.0.0.1 --port 8000
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For local development from this repo:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
PYTHONPATH=src python -m memtask start --transport stdio
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Useful HTTP commands: `memtask status` and `memtask stop`.
|
|
51
|
+
|
|
52
|
+
## Storage
|
|
53
|
+
|
|
54
|
+
Runtime state in this repo is stored in `data/tasks.sqlite`.
|
|
55
|
+
|
|
56
|
+
When installed outside this repo, MemTask uses `~/.memtask/tasks.sqlite` by default. Set `MEMTASK_DB_PATH` to choose a specific SQLite path.
|
|
57
|
+
|
|
58
|
+
The server creates the required SQLite tables on startup using `CREATE TABLE IF NOT EXISTS`. There is no migration framework.
|
|
59
|
+
|
|
60
|
+
## Tools
|
|
61
|
+
|
|
62
|
+
Task tools:
|
|
63
|
+
|
|
64
|
+
- `list_tasks`
|
|
65
|
+
- `get_task`
|
|
66
|
+
- `add_task`
|
|
67
|
+
- `add_batch_tasks`
|
|
68
|
+
- `complete_task`
|
|
69
|
+
- `remove_task`
|
|
70
|
+
- `remove_all_tasks`
|
|
71
|
+
- `current_tasks`
|
|
72
|
+
- `set_current_task`
|
|
73
|
+
|
|
74
|
+
Memory tools:
|
|
75
|
+
|
|
76
|
+
- `remember`
|
|
77
|
+
- `recall`
|
|
78
|
+
- `get_memory`
|
|
79
|
+
- `update_memory`
|
|
80
|
+
- `delete_memory`
|
|
81
|
+
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
Run tests:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
python -m pytest
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Compile-check the package:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
python -m py_compile src/memtask/*.py tests/*.py
|
|
94
|
+
```
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "memtask"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "A local MCP server for durable agent task state and lightweight memory."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"mcp",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.optional-dependencies]
|
|
16
|
+
test = [
|
|
17
|
+
"pytest>=8",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
memtask = "memtask.cli:main"
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.packages.find]
|
|
24
|
+
where = ["src"]
|
|
25
|
+
|
|
26
|
+
[tool.pytest.ini_options]
|
|
27
|
+
pythonpath = ["src"]
|
|
28
|
+
testpaths = ["tests"]
|
memtask-0.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from . import manager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
SERVER_INSTRUCTIONS = (
|
|
10
|
+
"Prefer `task_ref` over numeric task ids for follow-up calls. "
|
|
11
|
+
"Numeric ids can change after task mutations because pending tasks are displayed with ephemeral ids."
|
|
12
|
+
)
|
|
13
|
+
HELP_OVERVIEW = """# Local MCP Help
|
|
14
|
+
|
|
15
|
+
This server exposes a local SQLite-backed task and memory manager.
|
|
16
|
+
|
|
17
|
+
## Core Rule
|
|
18
|
+
|
|
19
|
+
Prefer `task_ref` for all follow-up task calls. It is the stable UUID alias returned by task-listing and task-fetching calls.
|
|
20
|
+
Use `pending_id` only as a temporary display id for humans.
|
|
21
|
+
|
|
22
|
+
## Common Workflow
|
|
23
|
+
|
|
24
|
+
1. Call `list_tasks`
|
|
25
|
+
2. Select a task's `task_ref`
|
|
26
|
+
3. Call `set_current_task(task_ref)` to start work
|
|
27
|
+
4. Call `complete_task(task_ref)` when finished
|
|
28
|
+
5. Call `remember(...)` to persist useful context
|
|
29
|
+
6. Recall with `recall(...)` and attach results with `memory_refs` when adding follow-up tasks
|
|
30
|
+
|
|
31
|
+
## Notes
|
|
32
|
+
|
|
33
|
+
- `current_tasks` only shows started pending tasks
|
|
34
|
+
- `complete_task` moves a task out of the pending list, so numeric ids can shift afterward
|
|
35
|
+
- `add_task` and `add_batch_tasks` return the new task records; capture their `task_ref` immediately if you plan to mutate them later
|
|
36
|
+
- `remove_all_tasks` deletes every currently listed pending task and is destructive.
|
|
37
|
+
- `remember` stores context with `memory_scope`, `kind`, and `confidence` (0-100)
|
|
38
|
+
- `recall` supports filtering by `memory_scope`, `kind`, minimum confidence, and text search via `query`
|
|
39
|
+
|
|
40
|
+
## Dependency Model
|
|
41
|
+
|
|
42
|
+
Tasks can optionally include `parent_task_refs` when created. Those are dependency edges used by the
|
|
43
|
+
manager for completion checks.
|
|
44
|
+
|
|
45
|
+
## Memory Graph
|
|
46
|
+
|
|
47
|
+
Memories can be organized with a single `parent_memory_id`.
|
|
48
|
+
Tasks can couple to memory via `memory_refs` for persistent context.
|
|
49
|
+
""".strip()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _json_text(value: object) -> str:
|
|
53
|
+
return json.dumps(value, indent=2, sort_keys=True, default=str)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _best_practice_for_tool(name: str) -> str | None:
|
|
57
|
+
if name in {"get_task", "set_current_task", "complete_task", "remove_task"}:
|
|
58
|
+
return "Prefer `task_ref`/UUID over numeric ids because pending ids can change after task mutations."
|
|
59
|
+
if name in {"list_tasks", "current_tasks"}:
|
|
60
|
+
return "Use this call to discover stable `task_ref` values before follow-up mutations."
|
|
61
|
+
if name in {"add_task", "add_batch_tasks"}:
|
|
62
|
+
return "Capture the returned `task_ref` values immediately if you will mutate these tasks later."
|
|
63
|
+
if name in {"remember", "recall", "update_memory"}:
|
|
64
|
+
return "Use `memory_scope` for context boundaries and `confidence` to represent belief strength."
|
|
65
|
+
if name == "remove_all_tasks":
|
|
66
|
+
return "This tool is destructive and removes all pending tasks returned by `list_tasks`."
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _example_for_tool(name: str) -> dict[str, Any] | list[dict[str, Any]] | None:
|
|
71
|
+
if name == "list_tasks":
|
|
72
|
+
return {}
|
|
73
|
+
if name == "current_tasks":
|
|
74
|
+
return {}
|
|
75
|
+
if name == "get_task":
|
|
76
|
+
return {"task_id": "task_ref-from-list_tasks"}
|
|
77
|
+
if name == "add_task":
|
|
78
|
+
return {"description": "Write release notes", "project": "ops", "tags": ["docs"]}
|
|
79
|
+
if name == "add_batch_tasks":
|
|
80
|
+
return {
|
|
81
|
+
"descriptions": ["Draft agenda", "Send recap"],
|
|
82
|
+
"project": "meetings",
|
|
83
|
+
"tags": ["team"],
|
|
84
|
+
"parent_task_refs": ["parent-task-ref"],
|
|
85
|
+
}
|
|
86
|
+
if name == "remember":
|
|
87
|
+
return {
|
|
88
|
+
"content": "Release notes should include API migration commands first.",
|
|
89
|
+
"memory_scope": "projects",
|
|
90
|
+
"kind": "fact",
|
|
91
|
+
"confidence": 90,
|
|
92
|
+
}
|
|
93
|
+
if name == "recall":
|
|
94
|
+
return {
|
|
95
|
+
"query": "release notes",
|
|
96
|
+
"memory_scope": "projects",
|
|
97
|
+
"min_confidence": 70,
|
|
98
|
+
"limit": 5,
|
|
99
|
+
}
|
|
100
|
+
if name == "get_memory":
|
|
101
|
+
return {"memory_id": "memory-ref"}
|
|
102
|
+
if name == "update_memory":
|
|
103
|
+
return {
|
|
104
|
+
"memory_id": "memory-ref",
|
|
105
|
+
"confidence": 95,
|
|
106
|
+
"tags": ["evidence", "release"],
|
|
107
|
+
}
|
|
108
|
+
if name == "delete_memory":
|
|
109
|
+
return {"memory_id": "memory-ref"}
|
|
110
|
+
if name == "set_current_task":
|
|
111
|
+
return {"task_id": "task_ref-from-list_tasks"}
|
|
112
|
+
if name == "complete_task":
|
|
113
|
+
return {"task_id": "task_ref-from-current_tasks"}
|
|
114
|
+
if name == "remove_task":
|
|
115
|
+
return {"task_id": "task_ref-to-delete"}
|
|
116
|
+
if name == "remove_all_tasks":
|
|
117
|
+
return {}
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
async def _tool_help_payload(mcp: Any, name: str) -> dict[str, Any]:
|
|
122
|
+
tools = await mcp.list_tools()
|
|
123
|
+
for tool in tools:
|
|
124
|
+
if tool.name != name:
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
payload: dict[str, Any] = {
|
|
128
|
+
"name": tool.name,
|
|
129
|
+
"description": tool.description,
|
|
130
|
+
"input_schema": tool.inputSchema,
|
|
131
|
+
}
|
|
132
|
+
best_practice = _best_practice_for_tool(tool.name)
|
|
133
|
+
if best_practice is not None:
|
|
134
|
+
payload["best_practice"] = best_practice
|
|
135
|
+
example = _example_for_tool(tool.name)
|
|
136
|
+
if example is not None:
|
|
137
|
+
payload["example_arguments"] = example
|
|
138
|
+
return payload
|
|
139
|
+
|
|
140
|
+
raise ValueError(f"Unknown tool: {name}")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def register(mcp: Any, manager_module: Any = manager) -> Any:
|
|
144
|
+
@mcp.resource(
|
|
145
|
+
"help://overview",
|
|
146
|
+
name="help_overview",
|
|
147
|
+
title="Local MCP Overview",
|
|
148
|
+
description="Overview, invariants, and common workflows for the local task manager MCP server.",
|
|
149
|
+
mime_type="text/markdown",
|
|
150
|
+
)
|
|
151
|
+
def help_overview() -> str:
|
|
152
|
+
return HELP_OVERVIEW
|
|
153
|
+
|
|
154
|
+
@mcp.resource(
|
|
155
|
+
"help://tools",
|
|
156
|
+
name="help_tools",
|
|
157
|
+
title="Local MCP Tool Catalog",
|
|
158
|
+
description="Live catalog of task-manager tools with best-practice notes.",
|
|
159
|
+
mime_type="application/json",
|
|
160
|
+
)
|
|
161
|
+
async def help_tools() -> str:
|
|
162
|
+
tools = await mcp.list_tools()
|
|
163
|
+
payload = []
|
|
164
|
+
for tool in tools:
|
|
165
|
+
payload.append(await _tool_help_payload(mcp, tool.name))
|
|
166
|
+
return _json_text(payload)
|
|
167
|
+
|
|
168
|
+
@mcp.resource(
|
|
169
|
+
"help://tool/{name}",
|
|
170
|
+
name="help_tool",
|
|
171
|
+
title="Local MCP Tool Help",
|
|
172
|
+
description="Detailed help for a specific task-manager tool.",
|
|
173
|
+
mime_type="application/json",
|
|
174
|
+
)
|
|
175
|
+
async def help_tool(name: str) -> str:
|
|
176
|
+
return _json_text(await _tool_help_payload(mcp, name))
|
|
177
|
+
|
|
178
|
+
@mcp.tool()
|
|
179
|
+
def list_tasks() -> str:
|
|
180
|
+
"""List pending tasks from Agent Task Manager with stable `task_ref` values."""
|
|
181
|
+
return _json_text(manager_module.list_tasks())
|
|
182
|
+
|
|
183
|
+
@mcp.tool()
|
|
184
|
+
def get_task(task_id: str) -> str:
|
|
185
|
+
"""Get a single task by task_ref/UUID or pending numeric id. UUID is preferred."""
|
|
186
|
+
return _json_text(manager_module.get_task(task_id))
|
|
187
|
+
|
|
188
|
+
@mcp.tool()
|
|
189
|
+
def add_task(
|
|
190
|
+
description: str,
|
|
191
|
+
project: str | None = None,
|
|
192
|
+
tags: list[str] | None = None,
|
|
193
|
+
parent_task_refs: list[str] | None = None,
|
|
194
|
+
memory_refs: list[str] | None = None,
|
|
195
|
+
) -> str:
|
|
196
|
+
"""Add a new task to Agent Task Manager."""
|
|
197
|
+
return _json_text(
|
|
198
|
+
manager_module.add_task(
|
|
199
|
+
description=description,
|
|
200
|
+
project=project,
|
|
201
|
+
tags=tags,
|
|
202
|
+
parent_task_refs=parent_task_refs,
|
|
203
|
+
memory_refs=memory_refs,
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
@mcp.tool()
|
|
208
|
+
def add_batch_tasks(
|
|
209
|
+
descriptions: list[str],
|
|
210
|
+
project: str | None = None,
|
|
211
|
+
tags: list[str] | None = None,
|
|
212
|
+
parent_task_refs: list[str] | None = None,
|
|
213
|
+
memory_refs: list[str] | None = None,
|
|
214
|
+
) -> str:
|
|
215
|
+
"""Add multiple tasks to Agent Task Manager."""
|
|
216
|
+
return _json_text(
|
|
217
|
+
manager_module.add_batch_tasks(
|
|
218
|
+
descriptions=descriptions,
|
|
219
|
+
project=project,
|
|
220
|
+
tags=tags,
|
|
221
|
+
parent_task_refs=parent_task_refs,
|
|
222
|
+
memory_refs=memory_refs,
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
@mcp.tool()
|
|
227
|
+
def complete_task(task_id: str) -> str:
|
|
228
|
+
"""Complete a task by task_ref/UUID or pending numeric id. UUID is preferred."""
|
|
229
|
+
return _json_text(manager_module.complete_task(task_id))
|
|
230
|
+
|
|
231
|
+
@mcp.tool()
|
|
232
|
+
def remove_task(task_id: str) -> str:
|
|
233
|
+
"""Delete a task by task_ref/UUID or pending numeric id. UUID is preferred."""
|
|
234
|
+
return _json_text(manager_module.remove_task(task_id))
|
|
235
|
+
|
|
236
|
+
@mcp.tool()
|
|
237
|
+
def remove_all_tasks() -> str:
|
|
238
|
+
"""Delete all pending tasks currently returned by `list_tasks`."""
|
|
239
|
+
return _json_text(manager_module.remove_all_tasks())
|
|
240
|
+
|
|
241
|
+
@mcp.tool()
|
|
242
|
+
def current_tasks() -> str:
|
|
243
|
+
"""List currently active tasks with stable `task_ref` values."""
|
|
244
|
+
return _json_text(manager_module.current_tasks())
|
|
245
|
+
|
|
246
|
+
@mcp.tool()
|
|
247
|
+
def set_current_task(task_id: str) -> str:
|
|
248
|
+
"""Stop active tasks and mark the specified task as current. UUID is preferred."""
|
|
249
|
+
return _json_text(manager_module.set_current_task(task_id))
|
|
250
|
+
|
|
251
|
+
@mcp.tool()
|
|
252
|
+
def remember(
|
|
253
|
+
content: str,
|
|
254
|
+
memory_scope: str | None = None,
|
|
255
|
+
kind: str | None = None,
|
|
256
|
+
confidence: int = 100,
|
|
257
|
+
parent_memory_id: str | None = None,
|
|
258
|
+
tags: list[str] | None = None,
|
|
259
|
+
) -> str:
|
|
260
|
+
"""Store a memory artifact with optional scope, kind, and confidence."""
|
|
261
|
+
return _json_text(
|
|
262
|
+
manager_module.remember(
|
|
263
|
+
content=content,
|
|
264
|
+
memory_scope=memory_scope,
|
|
265
|
+
kind=kind,
|
|
266
|
+
confidence=confidence,
|
|
267
|
+
parent_memory_id=parent_memory_id,
|
|
268
|
+
tags=tags,
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
@mcp.tool()
|
|
273
|
+
def recall(
|
|
274
|
+
query: str | None = None,
|
|
275
|
+
memory_scope: str | None = None,
|
|
276
|
+
kind: str | None = None,
|
|
277
|
+
min_confidence: int | None = None,
|
|
278
|
+
parent_memory_id: str | None = None,
|
|
279
|
+
limit: int | None = None,
|
|
280
|
+
) -> str:
|
|
281
|
+
"""Recall memory records by scope, kind, and confidence."""
|
|
282
|
+
return _json_text(
|
|
283
|
+
manager_module.recall(
|
|
284
|
+
query=query,
|
|
285
|
+
memory_scope=memory_scope,
|
|
286
|
+
kind=kind,
|
|
287
|
+
min_confidence=min_confidence,
|
|
288
|
+
parent_memory_id=parent_memory_id,
|
|
289
|
+
limit=limit,
|
|
290
|
+
)
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
@mcp.tool()
|
|
294
|
+
def get_memory(memory_id: str) -> str:
|
|
295
|
+
"""Get a memory by memory_id."""
|
|
296
|
+
return _json_text(manager_module.get_memory(memory_id))
|
|
297
|
+
|
|
298
|
+
@mcp.tool()
|
|
299
|
+
def update_memory(
|
|
300
|
+
memory_id: str,
|
|
301
|
+
content: str | None = None,
|
|
302
|
+
memory_scope: str | None = None,
|
|
303
|
+
kind: str | None = None,
|
|
304
|
+
confidence: int | None = None,
|
|
305
|
+
parent_memory_id: str | None = None,
|
|
306
|
+
clear_parent: bool = False,
|
|
307
|
+
tags: list[str] | None = None,
|
|
308
|
+
) -> str:
|
|
309
|
+
"""Update a memory with optional field replacements."""
|
|
310
|
+
return _json_text(
|
|
311
|
+
manager_module.update_memory(
|
|
312
|
+
memory_id=memory_id,
|
|
313
|
+
content=content,
|
|
314
|
+
memory_scope=memory_scope,
|
|
315
|
+
kind=kind,
|
|
316
|
+
confidence=confidence,
|
|
317
|
+
parent_memory_id=parent_memory_id,
|
|
318
|
+
clear_parent=clear_parent,
|
|
319
|
+
tags=tags,
|
|
320
|
+
)
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
@mcp.tool()
|
|
324
|
+
def delete_memory(memory_id: str) -> str:
|
|
325
|
+
"""Delete a memory by memory_id."""
|
|
326
|
+
return _json_text(manager_module.delete_memory(memory_id))
|
|
327
|
+
|
|
328
|
+
return mcp
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .api import SERVER_INSTRUCTIONS, register
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _load_mcp() -> Any:
|
|
10
|
+
try:
|
|
11
|
+
from mcp.server.fastmcp import FastMCP
|
|
12
|
+
|
|
13
|
+
return FastMCP
|
|
14
|
+
except ModuleNotFoundError as exc:
|
|
15
|
+
raise RuntimeError(
|
|
16
|
+
"Missing dependency: 'mcp'. Install it in the active Python environment "
|
|
17
|
+
"(for example: pip install mcp) before running this server."
|
|
18
|
+
) from exc
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def create_mcp(server_name: str = "Mini Demo Server") -> Any:
|
|
22
|
+
FastMCP = _load_mcp()
|
|
23
|
+
mcp = FastMCP(name=server_name, instructions=SERVER_INSTRUCTIONS)
|
|
24
|
+
return register(mcp)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def run_server(
|
|
28
|
+
transport: str = "stdio",
|
|
29
|
+
host: str = "127.0.0.1",
|
|
30
|
+
port: int = 8000,
|
|
31
|
+
server_name: str = "Mini Demo Server",
|
|
32
|
+
) -> None:
|
|
33
|
+
mcp = create_mcp(server_name=server_name)
|
|
34
|
+
|
|
35
|
+
if transport == "stdio":
|
|
36
|
+
mcp.run()
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
if transport != "streamable-http":
|
|
40
|
+
raise ValueError(f"Unsupported transport: {transport}")
|
|
41
|
+
|
|
42
|
+
mcp.settings.host = host
|
|
43
|
+
mcp.settings.port = port
|
|
44
|
+
mcp.settings.json_response = True
|
|
45
|
+
mcp.run(transport="streamable-http")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def main() -> None:
|
|
49
|
+
parser = argparse.ArgumentParser()
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"--transport",
|
|
52
|
+
choices=("stdio", "streamable-http"),
|
|
53
|
+
default="stdio",
|
|
54
|
+
)
|
|
55
|
+
parser.add_argument("--host", default="127.0.0.1")
|
|
56
|
+
parser.add_argument("--port", type=int, default=8000)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--server-name",
|
|
59
|
+
default="Mini Demo Server",
|
|
60
|
+
help="Override the MCP server name",
|
|
61
|
+
)
|
|
62
|
+
args = parser.parse_args()
|
|
63
|
+
|
|
64
|
+
run_server(
|
|
65
|
+
transport=args.transport,
|
|
66
|
+
host=args.host,
|
|
67
|
+
port=args.port,
|
|
68
|
+
server_name=args.server_name,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
main()
|