Skip to content

Commit b5a1861

Browse files
committed
feat: introduce agent presence management and add vision/sketch fields and tools to context
1 parent cc7da1a commit b5a1861

7 files changed

Lines changed: 400 additions & 211 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,6 @@ build/
4242

4343
# Local development
4444
context/
45+
46+
# DOC
47+
docs/plan

IMPLEMENTATION_VERSION_TRACKING.md

Lines changed: 0 additions & 185 deletions
This file was deleted.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "ccontext-mcp"
7-
version = "0.1.4"
7+
version = "0.1.5"
88
description = "MCP server for AI agents to manage project execution context"
99
readme = "README.md"
1010
license = "MIT"

src/ccontext_mcp/schema.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ class MilestoneStatus(str, Enum):
1818
PENDING = "pending"
1919

2020

21+
class AgentStatus(str, Enum):
22+
"""Agent presence status values."""
23+
ACTIVE = "active"
24+
IDLE = "idle"
25+
OFFLINE = "offline"
26+
27+
2128
class TaskStatus(str, Enum):
2229
"""Task status values."""
2330
PLANNED = "planned"
@@ -124,11 +131,36 @@ def progress(self) -> float:
124131

125132
class Context(BaseModel):
126133
"""The main context structure stored in context.yaml."""
134+
vision: Optional[str] = Field(default=None, description="Project vision/goal - what we're building")
135+
sketch: Optional[str] = Field(default=None, description="Execution blueprint - architecture, current phase, upcoming work (markdown)")
127136
milestones: list[Milestone] = Field(default_factory=list, description="Project milestones")
128137
notes: list[Note] = Field(default_factory=list, description="Notes")
129138
references: list[Reference] = Field(default_factory=list, description="References")
130139

131140

141+
# =============================================================================
142+
# Presence Models (stored in presence.yaml, gitignored)
143+
# =============================================================================
144+
145+
class AgentPresence(BaseModel):
146+
"""A single agent's presence status."""
147+
id: str = Field(..., description="Agent ID (e.g., peer-a, peer-b)")
148+
status: AgentStatus = Field(default=AgentStatus.IDLE, description="Current status")
149+
task: Optional[str] = Field(default=None, description="Current task ID (e.g., T003)")
150+
step: Optional[str] = Field(default=None, description="Current step ID (e.g., S2)")
151+
message: Optional[str] = Field(default=None, description="Free-form status message")
152+
updated_at: str = Field(
153+
default_factory=lambda: datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
154+
description="Last update timestamp"
155+
)
156+
157+
158+
class PresenceData(BaseModel):
159+
"""The presence data structure stored in presence.yaml."""
160+
agents: list[AgentPresence] = Field(default_factory=list, description="Agent presence list")
161+
heartbeat_timeout_seconds: int = Field(default=300, description="Timeout for stale detection")
162+
163+
132164
# =============================================================================
133165
# Response Models
134166
# =============================================================================
@@ -143,6 +175,8 @@ class TasksSummary(BaseModel):
143175

144176
class ContextResponse(BaseModel):
145177
"""Response structure for get_context tool."""
178+
vision: Optional[str] = None
179+
sketch: Optional[str] = None
146180
milestones: list[dict] = Field(default_factory=list)
147181
notes: list[dict] = Field(default_factory=list)
148182
references: list[dict] = Field(default_factory=list)

src/ccontext_mcp/server.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,69 @@ async def list_tools() -> list[Tool]:
3030
"required": [],
3131
},
3232
),
33+
# Vision/Sketch tools
34+
Tool(
35+
name="update_vision",
36+
description=TOOL_DESCRIPTIONS["update_vision"],
37+
inputSchema={
38+
"type": "object",
39+
"properties": {
40+
"vision": {"type": "string", "description": "Project vision statement"},
41+
},
42+
"required": ["vision"],
43+
},
44+
),
45+
Tool(
46+
name="update_sketch",
47+
description=TOOL_DESCRIPTIONS["update_sketch"],
48+
inputSchema={
49+
"type": "object",
50+
"properties": {
51+
"sketch": {"type": "string", "description": "Execution blueprint (markdown)"},
52+
},
53+
"required": ["sketch"],
54+
},
55+
),
56+
# Presence tools
57+
Tool(
58+
name="get_presence",
59+
description=TOOL_DESCRIPTIONS["get_presence"],
60+
inputSchema={
61+
"type": "object",
62+
"properties": {},
63+
"required": [],
64+
},
65+
),
66+
Tool(
67+
name="update_my_status",
68+
description=TOOL_DESCRIPTIONS["update_my_status"],
69+
inputSchema={
70+
"type": "object",
71+
"properties": {
72+
"agent_id": {"type": "string", "description": "Your agent ID (e.g., peer-a)"},
73+
"status": {
74+
"type": "string",
75+
"enum": ["active", "idle", "offline"],
76+
"description": "Your current status",
77+
},
78+
"task": {"type": "string", "description": "Current task ID (e.g., T003)"},
79+
"step": {"type": "string", "description": "Current step ID (e.g., S2)"},
80+
"message": {"type": "string", "description": "Status message"},
81+
},
82+
"required": ["agent_id", "status"],
83+
},
84+
),
85+
Tool(
86+
name="set_idle",
87+
description=TOOL_DESCRIPTIONS["set_idle"],
88+
inputSchema={
89+
"type": "object",
90+
"properties": {
91+
"agent_id": {"type": "string", "description": "Your agent ID (e.g., peer-a)"},
92+
},
93+
"required": ["agent_id"],
94+
},
95+
),
3396
# Milestone tools
3497
Tool(
3598
name="create_milestone",
@@ -283,6 +346,26 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
283346
if name == "get_context":
284347
result = tools.get_context()
285348

349+
# Vision/Sketch tools
350+
elif name == "update_vision":
351+
result = tools.update_vision(vision=arguments["vision"])
352+
elif name == "update_sketch":
353+
result = tools.update_sketch(sketch=arguments["sketch"])
354+
355+
# Presence tools
356+
elif name == "get_presence":
357+
result = tools.get_presence()
358+
elif name == "update_my_status":
359+
result = tools.update_my_status(
360+
agent_id=arguments["agent_id"],
361+
status=arguments["status"],
362+
task=arguments.get("task"),
363+
step=arguments.get("step"),
364+
message=arguments.get("message"),
365+
)
366+
elif name == "set_idle":
367+
result = tools.set_idle(agent_id=arguments["agent_id"])
368+
286369
# Milestone tools
287370
elif name == "create_milestone":
288371
result = tools.create_milestone(

0 commit comments

Comments
 (0)