agent-context-graph 0.3.0__tar.gz → 0.3.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.
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/PKG-INFO +3 -3
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/pyproject.toml +3 -3
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/mcp_server.py +20 -7
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/tests/test_tools.py +35 -17
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/.gitignore +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/CONTEXT.md +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/LICENSE +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/README.md +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/docs/adr/0001-runtime-adapter-terminology.md +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/docs/adr/0002-config-file-only-hook-resolution.md +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/docs/adr/0003-config-path-override.md +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/docs/adr/0004-user-id-in-session-start-event.md +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/docs/adr/0005-config-file-covers-llm-key-and-subprocess-env.md +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/docs/command-hooks.md +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/__init__.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/adapters/__init__.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/adapters/_identity.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/adapters/claude.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/adapters/claude_code.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/adapters/codex.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/adapters/openai.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/cli.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/events.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/hooks/__init__.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/hooks/cli.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/hooks/runner.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/hooks/runtime_plugin.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/link.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/protocols.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/tools.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/tests/__init__.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/tests/test_claude_code_adapter.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/tests/test_codex_adapter.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/tests/test_config_cli.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/tests/test_events.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/tests/test_hook_cli.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/tests/test_identity.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/tests/test_link.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/tests/test_runner_sessions_graph_connector.py +0 -0
- {agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/tests/test_runtime_plugin.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agent-context-graph
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Connect agent SDKs to context-graph components (actions-graph, skills-graph, etc.)
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -13,11 +13,11 @@ Requires-Dist: memgraph-toolbox>=0.1.11
|
|
|
13
13
|
Provides-Extra: claude
|
|
14
14
|
Requires-Dist: claude-agent-sdk>=0.1.0; extra == 'claude'
|
|
15
15
|
Provides-Extra: mcp
|
|
16
|
-
Requires-Dist: mcp
|
|
16
|
+
Requires-Dist: mcp<3,>=1.23.0; extra == 'mcp'
|
|
17
17
|
Provides-Extra: openai
|
|
18
18
|
Requires-Dist: openai-agents>=0.1.0; extra == 'openai'
|
|
19
19
|
Provides-Extra: test
|
|
20
|
-
Requires-Dist: mcp
|
|
20
|
+
Requires-Dist: mcp<3,>=1.23.0; extra == 'test'
|
|
21
21
|
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'test'
|
|
22
22
|
Requires-Dist: pytest>=9.0.3; extra == 'test'
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "agent-context-graph"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.1"
|
|
4
4
|
description = "Connect agent SDKs to context-graph components (actions-graph, skills-graph, etc.)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -27,13 +27,13 @@ claude = [
|
|
|
27
27
|
"claude-agent-sdk>=0.1.0",
|
|
28
28
|
]
|
|
29
29
|
mcp = [
|
|
30
|
-
"mcp>=1.23.0",
|
|
30
|
+
"mcp>=1.23.0,<3",
|
|
31
31
|
]
|
|
32
32
|
openai = [
|
|
33
33
|
"openai-agents>=0.1.0",
|
|
34
34
|
]
|
|
35
35
|
test = [
|
|
36
|
-
"mcp>=1.23.0",
|
|
36
|
+
"mcp>=1.23.0,<3",
|
|
37
37
|
"pytest>=9.0.3",
|
|
38
38
|
"pytest-asyncio>=0.24.0",
|
|
39
39
|
]
|
{agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/mcp_server.py
RENAMED
|
@@ -23,7 +23,11 @@ SERVER_NAME = "context-graph"
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def build_server(tools: dict[str, Tool] | None = None) -> Server:
|
|
26
|
-
"""An MCP server exposing ``tools``, all registered tools by default.
|
|
26
|
+
"""An MCP server exposing ``tools``, all registered tools by default.
|
|
27
|
+
|
|
28
|
+
Supports mcp 1.x, which registers handlers through decorators, and 2.x,
|
|
29
|
+
which takes them as constructor arguments; the protocol is the same.
|
|
30
|
+
"""
|
|
27
31
|
import mcp.types as types
|
|
28
32
|
from anyio import to_thread
|
|
29
33
|
from mcp.server.lowlevel import Server
|
|
@@ -31,28 +35,37 @@ def build_server(tools: dict[str, Tool] | None = None) -> Server:
|
|
|
31
35
|
from agent_context_graph.adapters._identity import load_config
|
|
32
36
|
|
|
33
37
|
tools = load_tools() if tools is None else tools
|
|
34
|
-
server: Server = Server(SERVER_NAME)
|
|
35
38
|
|
|
36
|
-
@server.list_tools()
|
|
37
39
|
async def list_tools() -> list[types.Tool]:
|
|
38
40
|
return [
|
|
39
41
|
types.Tool(name=tool.name, description=tool.description, inputSchema=tool.input_schema)
|
|
40
42
|
for tool in tools.values()
|
|
41
43
|
]
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
async def call_tool(name: str, arguments: dict[str, Any]) -> types.CallToolResult:
|
|
45
|
+
async def call_tool(name: str, arguments: dict[str, Any] | None) -> types.CallToolResult:
|
|
45
46
|
tool = tools.get(name)
|
|
46
47
|
if tool is None:
|
|
47
48
|
return _error(f"Unknown tool: {name}")
|
|
48
49
|
try:
|
|
49
50
|
# Tools query Memgraph synchronously; a thread keeps the server responsive.
|
|
50
|
-
result = await to_thread.run_sync(tool.call, arguments, load_config())
|
|
51
|
+
result = await to_thread.run_sync(tool.call, arguments or {}, load_config())
|
|
51
52
|
except ToolError as exc:
|
|
52
53
|
return _error(str(exc))
|
|
53
54
|
return types.CallToolResult(content=[types.TextContent(type="text", text=result.text)])
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
if hasattr(Server, "call_tool"): # mcp 1.x
|
|
57
|
+
server: Server = Server(SERVER_NAME)
|
|
58
|
+
server.list_tools()(list_tools)
|
|
59
|
+
server.call_tool()(call_tool)
|
|
60
|
+
return server
|
|
61
|
+
|
|
62
|
+
async def on_list_tools(ctx: Any, params: Any) -> types.ListToolsResult:
|
|
63
|
+
return types.ListToolsResult(tools=await list_tools())
|
|
64
|
+
|
|
65
|
+
async def on_call_tool(ctx: Any, params: Any) -> types.CallToolResult:
|
|
66
|
+
return await call_tool(params.name, params.arguments)
|
|
67
|
+
|
|
68
|
+
return Server(SERVER_NAME, on_list_tools=on_list_tools, on_call_tool=on_call_tool)
|
|
56
69
|
|
|
57
70
|
|
|
58
71
|
def serve() -> int:
|
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import io
|
|
11
11
|
import json
|
|
12
|
+
from contextlib import asynccontextmanager
|
|
12
13
|
from dataclasses import dataclass
|
|
13
14
|
from typing import Any, ClassVar
|
|
14
15
|
|
|
@@ -58,38 +59,55 @@ def tools(monkeypatch):
|
|
|
58
59
|
return registered
|
|
59
60
|
|
|
60
61
|
|
|
62
|
+
@asynccontextmanager
|
|
63
|
+
async def _connected(server):
|
|
64
|
+
"""A client session talking to ``server`` in memory, the same way under mcp 1.x and 2.x."""
|
|
65
|
+
import anyio
|
|
66
|
+
from mcp import ClientSession
|
|
67
|
+
from mcp.shared.memory import create_client_server_memory_streams
|
|
68
|
+
|
|
69
|
+
async with (
|
|
70
|
+
create_client_server_memory_streams() as (client_streams, server_streams),
|
|
71
|
+
anyio.create_task_group() as tg,
|
|
72
|
+
):
|
|
73
|
+
tg.start_soon(lambda: server.run(*server_streams, server.create_initialization_options()))
|
|
74
|
+
async with ClientSession(*client_streams) as client:
|
|
75
|
+
await client.initialize()
|
|
76
|
+
yield client
|
|
77
|
+
tg.cancel_scope.cancel()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _wire(model) -> dict:
|
|
81
|
+
"""A result as the harness receives it: mcp 2.x renamed the Python fields, not the JSON."""
|
|
82
|
+
return model.model_dump(by_alias=True, exclude_none=True)
|
|
83
|
+
|
|
84
|
+
|
|
61
85
|
@pytest.mark.asyncio
|
|
62
86
|
async def test_mcp_lists_tools_and_returns_only_the_text(tools):
|
|
63
87
|
"""Text only: Claude Code and Codex both show structuredContent instead of the text when both are sent."""
|
|
64
88
|
pytest.importorskip("mcp")
|
|
65
|
-
from mcp.shared.memory import create_connected_server_and_client_session
|
|
66
|
-
from mcp.types import TextContent
|
|
67
|
-
|
|
68
89
|
from agent_context_graph.mcp_server import build_server
|
|
69
90
|
|
|
70
|
-
async with
|
|
71
|
-
listed = await client.list_tools()
|
|
72
|
-
result = await client.call_tool("recall", {"question": "deploy day"})
|
|
91
|
+
async with _connected(build_server(tools)) as client:
|
|
92
|
+
listed = _wire(await client.list_tools())
|
|
93
|
+
result = _wire(await client.call_tool("recall", {"question": "deploy day"}))
|
|
73
94
|
|
|
74
|
-
assert [(tool
|
|
75
|
-
assert not result.isError
|
|
76
|
-
assert result
|
|
77
|
-
assert
|
|
95
|
+
assert [(tool["name"], tool["inputSchema"]["required"]) for tool in listed["tools"]] == [("recall", ["question"])]
|
|
96
|
+
assert not result.get("isError")
|
|
97
|
+
assert result["content"] == [{"type": "text", "text": "rows for deploy day of ante"}]
|
|
98
|
+
assert "structuredContent" not in result
|
|
78
99
|
|
|
79
100
|
|
|
80
101
|
@pytest.mark.asyncio
|
|
81
102
|
async def test_mcp_reports_a_tool_error_to_the_model(tools):
|
|
82
103
|
pytest.importorskip("mcp")
|
|
83
|
-
from mcp.shared.memory import create_connected_server_and_client_session
|
|
84
|
-
from mcp.types import TextContent
|
|
85
|
-
|
|
86
104
|
from agent_context_graph.mcp_server import build_server
|
|
87
105
|
|
|
88
|
-
async with
|
|
89
|
-
result = await client.call_tool("recall", {"question": "fail"})
|
|
106
|
+
async with _connected(build_server(tools)) as client:
|
|
107
|
+
result = _wire(await client.call_tool("recall", {"question": "fail"}))
|
|
90
108
|
|
|
91
|
-
assert result
|
|
92
|
-
assert result
|
|
109
|
+
assert result["isError"]
|
|
110
|
+
assert result["content"] == [{"type": "text", "text": "no user configured"}]
|
|
93
111
|
|
|
94
112
|
|
|
95
113
|
def test_recall_cli_prints_the_text_or_the_json(tools, capsys):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/docs/adr/0001-runtime-adapter-terminology.md
RENAMED
|
File without changes
|
|
File without changes
|
{agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/docs/adr/0003-config-path-override.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/adapters/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/adapters/claude.py
RENAMED
|
File without changes
|
|
File without changes
|
{agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/adapters/codex.py
RENAMED
|
File without changes
|
{agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/adapters/openai.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/hooks/__init__.py
RENAMED
|
File without changes
|
{agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/hooks/cli.py
RENAMED
|
File without changes
|
{agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/hooks/runner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_context_graph-0.3.0 → agent_context_graph-0.3.1}/src/agent_context_graph/protocols.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|