agentgraph-server 0.7.2__tar.gz → 0.9.0__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.
- agentgraph_server-0.9.0/.agents/skills/agentgraph/SKILL.md +54 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/.agents/skills/agentgraph/references/commands.md +12 -2
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/.agents/skills/agentgraph/references/data-model.md +6 -1
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/.agents/skills/agentgraph/references/operations.md +32 -16
- {agentgraph_server-0.7.2/agentgraph_server.egg-info → agentgraph_server-0.9.0}/PKG-INFO +14 -14
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/README.md +1 -1
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/backends/sqlite/backend.py +105 -31
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/cli.py +32 -7
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/cli_query.py +18 -1
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/connectors/base.py +75 -31
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/connectors/command_effects.py +10 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/connectors/feed.py +22 -7
- agentgraph_server-0.9.0/agentgraph/connectors/registry.py +175 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/connectors/status.py +7 -2
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/core/storage.py +27 -0
- agentgraph_server-0.9.0/agentgraph/graph/delete.py +102 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/graph/fetch.py +3 -1
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/graph/link.py +3 -2
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/graph/query.py +3 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/graph/upsert.py +7 -2
- agentgraph_server-0.9.0/agentgraph/mcp/models.py +131 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/mcp/server.py +299 -202
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/query_client.py +19 -1
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/graph_api.py +12 -1
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/meta_api.py +4 -4
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/static/viewer.html +187 -26
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/skills.py +22 -7
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0/agentgraph_server.egg-info}/PKG-INFO +14 -14
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph_server.egg-info/SOURCES.txt +3 -0
- agentgraph_server-0.9.0/agentgraph_server.egg-info/requires.txt +38 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/pyproject.toml +13 -13
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_auth.py +8 -9
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_browse.py +24 -1
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_cli.py +162 -11
- agentgraph_server-0.9.0/tests/test_connector_discovery.py +96 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_docs_build.py +15 -6
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_entity_types.py +66 -3
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_expiration.py +36 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_fetch.py +17 -2
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_mcp_transport.py +17 -4
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_observation.py +13 -0
- agentgraph_server-0.9.0/tests/test_observed_since.py +209 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_query.py +269 -89
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_query_client.py +2 -2
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_registry.py +78 -1
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_release_metadata.py +2 -2
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_route_resolution.py +1 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_rss_connector.py +197 -230
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_schema.py +51 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_upsert.py +44 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_viewer_layout_contract.py +95 -1
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_web_connector.py +23 -129
- agentgraph_server-0.7.2/.agents/skills/agentgraph/SKILL.md +0 -97
- agentgraph_server-0.7.2/agentgraph/connectors/registry.py +0 -86
- agentgraph_server-0.7.2/agentgraph/graph/delete.py +0 -40
- agentgraph_server-0.7.2/agentgraph_server.egg-info/requires.txt +0 -38
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/.agents/skills/slack-auth/SKILL.md +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/LICENSE +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/__init__.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/auth/__init__.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/auth/credentials.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/backends/__init__.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/backends/sqlite/__init__.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/backends/sqlite/schema.sql +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/backends/sqlite/vector.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/cli_sync.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/config.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/connectors/__init__.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/connectors/match_patterns.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/core/__init__.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/core/context.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/core/runtime.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/demo.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/demo_fixtures/reliable-webhooks.md +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/demo_fixtures/retry-guidance.md +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/graph/__init__.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/graph/bookmark.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/graph/download.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/graph/embeddings.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/graph/expiration.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/graph/operations.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/graph/person.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/logging.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/mcp/__init__.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/perf.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/__init__.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/app.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/browse_api.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/observation.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/presentation.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/router.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/sync.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/sync_api.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph/server/uds.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph_server.egg-info/dependency_links.txt +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph_server.egg-info/entry_points.txt +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/agentgraph_server.egg-info/top_level.txt +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/setup.cfg +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_auth_status_methods.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_benchmarks.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_command_effects.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_config.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_connectors.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_discord_attachments.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_embeddings.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_feed_connectors.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_graph_operations.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_launch_demo.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_logging.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_match_patterns.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_query_parity.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_router.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_server_imports.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_server_lifespan.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_server_uds.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_slack_auth_artifacts.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_slack_oauth.py +0 -0
- {agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/tests/test_sync.py +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentgraph
|
|
3
|
+
description: Use AgentGraph through its CLI or MCP tools to search and traverse selected local context, retrieve full source-backed evidence, fetch missing or stale resources, and troubleshoot connector availability.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# AgentGraph skill
|
|
7
|
+
|
|
8
|
+
AgentGraph is a local graph of selected messages, documents, people, feeds, pages,
|
|
9
|
+
and relationships. Use its CLI or connected MCP tools to investigate that context;
|
|
10
|
+
do not query its database or connector internals directly.
|
|
11
|
+
|
|
12
|
+
Prefer the connected MCP tools when AgentGraph is available through MCP. Use the
|
|
13
|
+
`agentgraph` CLI when MCP is unavailable or when a terminal script is the natural
|
|
14
|
+
interface. Do not query AgentGraph's SQLite database or connector internals directly.
|
|
15
|
+
|
|
16
|
+
## Investigate
|
|
17
|
+
|
|
18
|
+
1. Start with `search` for discovery. If the user supplied a graph ID, platform
|
|
19
|
+
reference, or indexed URL, use `get` directly instead.
|
|
20
|
+
2. Read the full entity before making a source-backed claim. Search results and graph
|
|
21
|
+
traversals contain bounded snippets.
|
|
22
|
+
3. Inspect direct edges or traverse only when relationships help answer the question.
|
|
23
|
+
4. Resolve a stub or fetch a known resource when missing or stale content blocks the
|
|
24
|
+
answer, then repeat the read.
|
|
25
|
+
5. Compare source dates and contents. Cite the source URL or entity ID and separate
|
|
26
|
+
source facts from conclusions drawn across sources.
|
|
27
|
+
|
|
28
|
+
Use search filters when the type, source, author, time window, observation, or
|
|
29
|
+
attachments are already known. A search without a query lists matching entities by
|
|
30
|
+
date; a query ranks them by relevance.
|
|
31
|
+
|
|
32
|
+
## Guardrails
|
|
33
|
+
|
|
34
|
+
- A stub has neither a title nor content.
|
|
35
|
+
- Use `source_created_at` and `source_updated_at` for source chronology; local
|
|
36
|
+
`created_at` and `updated_at` describe the graph record.
|
|
37
|
+
- Browser observation alone updates `observed_at`; fetch, poll, and ingest do not
|
|
38
|
+
indicate that the user viewed a resource.
|
|
39
|
+
- Check connector or authentication state only when availability, freshness, or a
|
|
40
|
+
graph operation calls it into question.
|
|
41
|
+
- Confirm Person merges, deletion, credential removal, and removal of bookmark
|
|
42
|
+
protection with the user.
|
|
43
|
+
- A multi-target deletion resolves every target before changing the graph; it either
|
|
44
|
+
deletes the resolved set or leaves the graph unchanged.
|
|
45
|
+
|
|
46
|
+
## References
|
|
47
|
+
|
|
48
|
+
- Read [references/commands.md](references/commands.md) for command syntax, MCP
|
|
49
|
+
mappings, filters, targets, stubs, and result limits.
|
|
50
|
+
- Read [references/data-model.md](references/data-model.md) for entity types,
|
|
51
|
+
relationships, attachments, timestamps, and retention.
|
|
52
|
+
- Read [references/operations.md](references/operations.md) for connector discovery,
|
|
53
|
+
authentication, fetching, downloads, polling, connector-owned commands, and server
|
|
54
|
+
transport.
|
{agentgraph_server-0.7.2 → agentgraph_server-0.9.0}/.agents/skills/agentgraph/references/commands.md
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## CLI
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
agentgraph search [
|
|
6
|
+
agentgraph search [--type <type>] [--platform <platform>] [--filter key=value] [--since 12h|30m|2d] [--observed-since 12h|30m|2d] [--mine] [--has-attachments] [--limit N] [--order-by created_at|updated_at|source_created_at|source_updated_at|observed_at|synced_at] [--min-score N] [--json]
|
|
7
7
|
agentgraph get <entity-id|platform/ref|url> [--resolve] [--json]
|
|
8
8
|
agentgraph edges <entity-id|platform/ref> [--type <edge-type>] [--direction in|out|both] [--json]
|
|
9
9
|
agentgraph traverse <entity-id|platform/ref> [--resolve] [--depth 0..4] [--json]
|
|
@@ -14,6 +14,11 @@ filter is applied as a hard constraint (`--limit` defaults to 10). Without one t
|
|
|
14
14
|
is no ranking: the filters select the entities, `--order-by` sorts them newest first,
|
|
15
15
|
`--min-score` is ignored, and `--limit` defaults to 50.
|
|
16
16
|
|
|
17
|
+
`--since` filters `updated_at`; `--observed-since` filters browser observation time
|
|
18
|
+
(`observed_at`), excluding never-observed entities. Both accept ISO timestamps or
|
|
19
|
+
relative durations and include the cutoff itself. When supplied together, both
|
|
20
|
+
must match. Neither flag changes ordering.
|
|
21
|
+
|
|
17
22
|
`get` and `traverse` do not resolve stubs unless `--resolve` is supplied. Depth 0
|
|
18
23
|
returns only the starting entity; depths 1 through 4 include that many relationship
|
|
19
24
|
hops.
|
|
@@ -28,7 +33,7 @@ accept full UUIDs, unambiguous UUID prefixes, and platform references.
|
|
|
28
33
|
## MCP equivalents
|
|
29
34
|
|
|
30
35
|
```text
|
|
31
|
-
agentgraph search ... -> search_entities_tool(query, entity_types, platform, filters, since, authored_by_me, has_attachments, limit, order_by, min_score, refresh)
|
|
36
|
+
agentgraph search ... -> search_entities_tool(query, entity_types, platform, filters, since, authored_by_me, has_attachments, limit, order_by, min_score, refresh, observed_since)
|
|
32
37
|
agentgraph get ... -> get_entity_tool(entity_id, resolve)
|
|
33
38
|
agentgraph edges ... -> get_edges_tool(entity_id, edge_type, direction)
|
|
34
39
|
agentgraph traverse ... -> traverse_graph_tool(entity_id, max_depth, resolve)
|
|
@@ -38,6 +43,11 @@ MCP search defaults to `refresh=false`; set it only when fresh connector-owned
|
|
|
38
43
|
presentation metadata is needed. It does not replace a targeted source fetch for
|
|
39
44
|
stale content.
|
|
40
45
|
|
|
46
|
+
MCP tools return structured content: successes use `status="ok"` with a `data`
|
|
47
|
+
object; expected failures use `status="error"`, an error `code`, and `message`, with
|
|
48
|
+
the MCP error flag set. Search `data` includes `has_more`; entity payloads include
|
|
49
|
+
`is_stub` and, when known, `source_url`.
|
|
50
|
+
|
|
41
51
|
## Stubs
|
|
42
52
|
|
|
43
53
|
An entity is a stub when it has neither a title nor content. With the CLI, pass
|
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
| `Task` | Tracked work items such as Jira issues. Issue key, status, and assignee live in `metadata`. |
|
|
15
15
|
| `Video` | Recorded videos such as Looms. The transcript is indexed as content and `metadata.web_url` links to the video. |
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Installed connectors may add shared entity type names such as `Project`. The complete
|
|
18
|
+
alphabetical catalog, including connector descriptions, is embedded in the
|
|
19
|
+
`search_entities_tool` MCP description. With the CLI, connector-specific declarations
|
|
20
|
+
are available from `agentgraph list-connectors --json`. Undeclared type strings remain
|
|
21
|
+
storable and searchable for compatibility, but do not receive automatic discovery or
|
|
22
|
+
reference routing.
|
|
18
23
|
|
|
19
24
|
## Relationships
|
|
20
25
|
|
|
@@ -10,10 +10,17 @@ agentgraph auth remove <provider> [--account <account-id>] [--json]
|
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
Use `agentgraph list-connectors --json` to discover installed connector source names,
|
|
13
|
-
valid platform values, URL ownership, polling
|
|
14
|
-
|
|
13
|
+
valid platform values, connector-declared entity types, URL ownership, polling
|
|
14
|
+
delegation, and sync state. Do not rely on a hardcoded platform list. MCP clients
|
|
15
|
+
receive the full installed entity type catalog in the `search_entities_tool`
|
|
16
|
+
description during tool discovery. Use `--verify` only when a live provider check is
|
|
15
17
|
needed.
|
|
16
18
|
|
|
19
|
+
Connector `last_synced_at` values come from successful poll timestamps in `sync_state`,
|
|
20
|
+
aggregated across account-scoped cursors. A connector command may reset its own cursor
|
|
21
|
+
through the generic command-effects path; after a reset, its status is `never` until the
|
|
22
|
+
next successful poll.
|
|
23
|
+
|
|
17
24
|
MCP equivalents are `list_connectors_tool`, `list_auth_providers_tool`,
|
|
18
25
|
`authenticate_provider_tool`, and `remove_auth_provider_tool`.
|
|
19
26
|
|
|
@@ -68,13 +75,15 @@ forms are `run_connector_command_tool("web", ["observe", "<url-or-prefix>"])` an
|
|
|
68
75
|
```bash
|
|
69
76
|
agentgraph download <entity-id|platform/ref> [--output <file-or-dir>] [--json]
|
|
70
77
|
agentgraph bookmark <entity-id|platform/ref|url> [--remove] [--json]
|
|
71
|
-
agentgraph delete <entity-id|platform/ref|url
|
|
78
|
+
agentgraph delete <entity-id|platform/ref|url>... [--json]
|
|
72
79
|
agentgraph unify-persons <primary-person-id> <duplicate-person-id>... [--json]
|
|
73
80
|
```
|
|
74
81
|
|
|
75
|
-
Use `download_entity_tool`, `bookmark_entity_tool`, `delete_entity_tool`,
|
|
76
|
-
`unify_persons_tool` through MCP. Confirm identity before
|
|
77
|
-
first Person is canonical and keeps its ID.
|
|
82
|
+
Use `download_entity_tool`, `bookmark_entity_tool`, `delete_entity_tool`,
|
|
83
|
+
`delete_entities_tool`, and `unify_persons_tool` through MCP. Confirm identity before
|
|
84
|
+
person unification. The first Person is canonical and keeps its ID. Multi-target
|
|
85
|
+
deletion resolves every target before deleting any of them, removes connected edges,
|
|
86
|
+
and returns compact references for the deleted entities.
|
|
78
87
|
|
|
79
88
|
## Server and skill setup
|
|
80
89
|
|
|
@@ -86,12 +95,17 @@ The server listens on a Unix socket (`AGENTGRAPH_SERVER_UDS_PATH`, default
|
|
|
86
95
|
`~/.agentgraph/agentgraph.sock`) as well as TCP, because most agent sandboxes deny
|
|
87
96
|
loopback TCP while permitting an allowlisted socket.
|
|
88
97
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
CLI polling and connector or authentication commands that queue a poll or ingest
|
|
99
|
+
contact the local server. Reads and `fetch`/`download` follow
|
|
100
|
+
`AGENTGRAPH_QUERY_TRANSPORT` (`auto` by default: socket, then TCP, then in-process),
|
|
101
|
+
so they keep working when the server is unreachable. The MCP server can run polling
|
|
102
|
+
and ingest locally when it cannot queue them on the local server.
|
|
103
|
+
|
|
104
|
+
Run ordinary reads with the default transport. To diagnose connectivity, use
|
|
105
|
+
`AGENTGRAPH_QUERY_TRANSPORT=server agentgraph search x --limit 1`; retry an affected
|
|
106
|
+
read with `AGENTGRAPH_QUERY_TRANSPORT=in-process` only after an actual sandbox
|
|
107
|
+
connection failure. See the Coding agent sandboxes section of the configuration docs
|
|
108
|
+
for Unix-socket allowlist settings.
|
|
95
109
|
|
|
96
110
|
```bash
|
|
97
111
|
agentgraph mcp-config
|
|
@@ -99,9 +113,11 @@ agentgraph mcp-serve
|
|
|
99
113
|
agentgraph install-skill [agentgraph] [--target user|project] [--no-claude] [--force] [--json]
|
|
100
114
|
```
|
|
101
115
|
|
|
102
|
-
`agentgraph mcp-config` prints local stdio setup for ChatGPT Desktop Work Mode
|
|
103
|
-
Claude Desktop
|
|
104
|
-
|
|
105
|
-
to its MCP
|
|
116
|
+
`agentgraph mcp-config` prints local stdio setup for ChatGPT Desktop Work Mode, Codex,
|
|
117
|
+
Claude Desktop, and Claude Code. Prefer these MCP connections when they are available.
|
|
118
|
+
In ChatGPT Desktop, enter the printed executable in **Command to launch** and
|
|
119
|
+
`mcp-serve` in **Arguments**; for Claude Desktop, add the printed JSON to its MCP
|
|
120
|
+
configuration file. Claude Code uses the printed user-scoped command so the server is
|
|
121
|
+
available across projects.
|
|
106
122
|
|
|
107
123
|
The skill installer is available only through the `agentgraph install-skill` CLI command.
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentgraph-server
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: A local-first CLI and MCP server that turns selected digital sources into a searchable graph for the AI agent you already use.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Requires-Dist: aiosqlite>=0.20.0
|
|
9
|
-
Requires-Dist: agentgraph-connector-web<0.
|
|
9
|
+
Requires-Dist: agentgraph-connector-web<0.8,>=0.7.0
|
|
10
10
|
Requires-Dist: apscheduler>=3.11.2
|
|
11
11
|
Requires-Dist: click<8.4
|
|
12
12
|
Requires-Dist: fastapi>=0.135.2
|
|
13
13
|
Requires-Dist: fastembed>=0.8.0
|
|
14
14
|
Requires-Dist: httpx>=0.28.1
|
|
15
|
-
Requires-Dist: mcp
|
|
15
|
+
Requires-Dist: mcp<2,>=1.26.0
|
|
16
16
|
Requires-Dist: numpy>=2.4.6
|
|
17
17
|
Requires-Dist: pydantic>=2.12.5
|
|
18
18
|
Requires-Dist: pydantic-settings>=2.13.1
|
|
@@ -22,21 +22,21 @@ Requires-Dist: sqlite-vec>=0.1.9
|
|
|
22
22
|
Requires-Dist: typer>=0.24.1
|
|
23
23
|
Requires-Dist: uvicorn>=0.42.0
|
|
24
24
|
Provides-Extra: google
|
|
25
|
-
Requires-Dist: agentgraph-connector-google<0.7,>=0.
|
|
25
|
+
Requires-Dist: agentgraph-connector-google<0.7,>=0.6.1; extra == "google"
|
|
26
26
|
Provides-Extra: slack
|
|
27
|
-
Requires-Dist: agentgraph-connector-slack<0.7,>=0.
|
|
27
|
+
Requires-Dist: agentgraph-connector-slack<0.7,>=0.6.1; extra == "slack"
|
|
28
28
|
Provides-Extra: discord
|
|
29
|
-
Requires-Dist: agentgraph-connector-discord<0.7,>=0.
|
|
29
|
+
Requires-Dist: agentgraph-connector-discord<0.7,>=0.6.1; extra == "discord"
|
|
30
30
|
Provides-Extra: rss
|
|
31
|
-
Requires-Dist: agentgraph-connector-rss<0.
|
|
31
|
+
Requires-Dist: agentgraph-connector-rss<0.8,>=0.7.0; extra == "rss"
|
|
32
32
|
Provides-Extra: web
|
|
33
|
-
Requires-Dist: agentgraph-connector-web<0.
|
|
33
|
+
Requires-Dist: agentgraph-connector-web<0.8,>=0.7.0; extra == "web"
|
|
34
34
|
Provides-Extra: all
|
|
35
|
-
Requires-Dist: agentgraph-connector-web<0.
|
|
36
|
-
Requires-Dist: agentgraph-connector-google<0.7,>=0.
|
|
37
|
-
Requires-Dist: agentgraph-connector-slack<0.7,>=0.
|
|
38
|
-
Requires-Dist: agentgraph-connector-discord<0.7,>=0.
|
|
39
|
-
Requires-Dist: agentgraph-connector-rss<0.
|
|
35
|
+
Requires-Dist: agentgraph-connector-web<0.8,>=0.7.0; extra == "all"
|
|
36
|
+
Requires-Dist: agentgraph-connector-google<0.7,>=0.6.1; extra == "all"
|
|
37
|
+
Requires-Dist: agentgraph-connector-slack<0.7,>=0.6.1; extra == "all"
|
|
38
|
+
Requires-Dist: agentgraph-connector-discord<0.7,>=0.6.1; extra == "all"
|
|
39
|
+
Requires-Dist: agentgraph-connector-rss<0.8,>=0.7.0; extra == "all"
|
|
40
40
|
Dynamic: license-file
|
|
41
41
|
|
|
42
42
|
# AgentGraph
|
|
@@ -122,7 +122,7 @@ Connect the agent you already use:
|
|
|
122
122
|
agentgraph mcp-config
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
Use the printed instructions to connect ChatGPT Desktop Work Mode
|
|
125
|
+
Use the printed instructions to connect ChatGPT Desktop Work Mode, Codex, Claude Desktop, or Claude Code to the local stdio server. MCP is the preferred connection; the installed skill provides CLI fallback workflows. See [Install](docs-src/install.md) for the complete setup path.
|
|
126
126
|
|
|
127
127
|
## What AgentGraph is and is not
|
|
128
128
|
|
|
@@ -81,7 +81,7 @@ Connect the agent you already use:
|
|
|
81
81
|
agentgraph mcp-config
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
Use the printed instructions to connect ChatGPT Desktop Work Mode
|
|
84
|
+
Use the printed instructions to connect ChatGPT Desktop Work Mode, Codex, Claude Desktop, or Claude Code to the local stdio server. MCP is the preferred connection; the installed skill provides CLI fallback workflows. See [Install](docs-src/install.md) for the complete setup path.
|
|
85
85
|
|
|
86
86
|
## What AgentGraph is and is not
|
|
87
87
|
|
|
@@ -68,6 +68,7 @@ _LIST_PAGE_ORDER_BY = {
|
|
|
68
68
|
}
|
|
69
69
|
_COLUMN_FILTERS = {"platform", "platform_entity_id", "entity_type"}
|
|
70
70
|
_FTS_DELETE_CHUNK_SIZE = 500
|
|
71
|
+
_PLATFORM_ENTITY_ID_LOOKUP_CHUNK_SIZE = 900
|
|
71
72
|
_BUSY_TIMEOUT_MS = 5_000
|
|
72
73
|
_SCHEMA_VERSION = 6
|
|
73
74
|
|
|
@@ -116,6 +117,7 @@ def _entity_predicates(
|
|
|
116
117
|
since: datetime | None = None,
|
|
117
118
|
authored_by: list[str] | None = None,
|
|
118
119
|
has_attachments: bool = False,
|
|
120
|
+
observed_since: datetime | None = None,
|
|
119
121
|
) -> tuple[str, list[Any], str, list[Any]]:
|
|
120
122
|
"""Build the entity-selection predicates shared by every leg of a search.
|
|
121
123
|
|
|
@@ -143,6 +145,9 @@ def _entity_predicates(
|
|
|
143
145
|
if since:
|
|
144
146
|
clauses.append("e.updated_at >= ?")
|
|
145
147
|
params.append(_utc_stamp(since))
|
|
148
|
+
if observed_since:
|
|
149
|
+
clauses.append("e.observed_at >= ?")
|
|
150
|
+
params.append(_utc_stamp(observed_since))
|
|
146
151
|
if has_attachments:
|
|
147
152
|
clauses.append(
|
|
148
153
|
"json_extract(e.metadata, '$.attachments') IS NOT NULL"
|
|
@@ -865,11 +870,14 @@ class SQLiteBackend(StorageBackend):
|
|
|
865
870
|
str(existing_row["content"]) if existing_row and existing_row["content"] else ""
|
|
866
871
|
)
|
|
867
872
|
fts_title = e.title if e.title is not None else existing_title
|
|
868
|
-
fts_content =
|
|
873
|
+
fts_content = (
|
|
874
|
+
e.content if e.content_searchable and e.content is not None else ""
|
|
875
|
+
)
|
|
869
876
|
rewrite_fts = (
|
|
870
877
|
existing_row is None
|
|
871
878
|
or fts_title != existing_title
|
|
872
|
-
or fts_content != existing_content
|
|
879
|
+
or (e.content_searchable and fts_content != existing_content)
|
|
880
|
+
or not e.content_searchable
|
|
873
881
|
)
|
|
874
882
|
embedding = embeddings.get(e.platform_entity_id)
|
|
875
883
|
emb_blob = pack_embedding(embedding) if embedding else None
|
|
@@ -888,7 +896,10 @@ class SQLiteBackend(StorageBackend):
|
|
|
888
896
|
target_title = e.title if e.title is not None else existing_row["title"]
|
|
889
897
|
target_content = e.content if e.content is not None else existing_row["content"]
|
|
890
898
|
target_embedding = (
|
|
891
|
-
emb_blob if emb_blob is not None
|
|
899
|
+
emb_blob if e.content_searchable and emb_blob is not None
|
|
900
|
+
else existing_row["content_embedding"]
|
|
901
|
+
if e.content_searchable
|
|
902
|
+
else None
|
|
892
903
|
)
|
|
893
904
|
target_source_created = (
|
|
894
905
|
source_created
|
|
@@ -923,7 +934,7 @@ class SQLiteBackend(StorageBackend):
|
|
|
923
934
|
entity_type = CASE WHEN entities.entity_type = 'Document' THEN EXCLUDED.entity_type ELSE entities.entity_type END,
|
|
924
935
|
title = COALESCE(EXCLUDED.title, entities.title),
|
|
925
936
|
content = COALESCE(EXCLUDED.content, entities.content),
|
|
926
|
-
content_embedding = COALESCE(EXCLUDED.content_embedding, entities.content_embedding),
|
|
937
|
+
content_embedding = CASE WHEN ? THEN COALESCE(EXCLUDED.content_embedding, entities.content_embedding) ELSE NULL END,
|
|
927
938
|
metadata = EXCLUDED.metadata,
|
|
928
939
|
source_created_at = COALESCE(EXCLUDED.source_created_at, entities.source_created_at),
|
|
929
940
|
source_updated_at = COALESCE(EXCLUDED.source_updated_at, entities.source_updated_at),
|
|
@@ -949,6 +960,7 @@ class SQLiteBackend(StorageBackend):
|
|
|
949
960
|
local_updated,
|
|
950
961
|
e.retention_policy,
|
|
951
962
|
parent_id,
|
|
963
|
+
e.content_searchable,
|
|
952
964
|
local_updated,
|
|
953
965
|
],
|
|
954
966
|
)
|
|
@@ -1216,42 +1228,55 @@ class SQLiteBackend(StorageBackend):
|
|
|
1216
1228
|
|
|
1217
1229
|
async def delete_entity(self, entity_id: str) -> EntityResult:
|
|
1218
1230
|
"""Delete one entity by internal ID. Edges cascade; FTS rows are removed explicitly."""
|
|
1231
|
+
deleted = await self.delete_entities([entity_id])
|
|
1232
|
+
return deleted[0]
|
|
1233
|
+
|
|
1234
|
+
async def delete_entities(self, entity_ids: list[str]) -> list[EntityResult]:
|
|
1235
|
+
"""Atomically delete entities, their edges, and their FTS rows."""
|
|
1236
|
+
unique_ids = list(dict.fromkeys(entity_ids))
|
|
1237
|
+
if not unique_ids:
|
|
1238
|
+
raise ValueError("At least one entity ID is required")
|
|
1219
1239
|
assert self._write_lock is not None
|
|
1220
1240
|
async with self._write_lock:
|
|
1221
1241
|
conn = self._conn_or_raise()
|
|
1222
1242
|
await conn.execute("BEGIN IMMEDIATE")
|
|
1223
1243
|
try:
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1244
|
+
rows: list[aiosqlite.Row] = []
|
|
1245
|
+
for start in range(0, len(unique_ids), _FTS_DELETE_CHUNK_SIZE):
|
|
1246
|
+
ids = unique_ids[start : start + _FTS_DELETE_CHUNK_SIZE]
|
|
1247
|
+
placeholders = ",".join("?" * len(ids))
|
|
1248
|
+
await conn.execute(
|
|
1249
|
+
f"""
|
|
1250
|
+
UPDATE entities
|
|
1251
|
+
SET retention_parent_id = NULL, updated_at = ?
|
|
1252
|
+
WHERE retention_parent_id IN ({placeholders}) AND bookmarked = 1
|
|
1253
|
+
""",
|
|
1254
|
+
[_now(), *ids],
|
|
1255
|
+
)
|
|
1256
|
+
cursor = await conn.execute(
|
|
1257
|
+
f"""
|
|
1258
|
+
DELETE FROM entities
|
|
1259
|
+
WHERE id IN ({placeholders})
|
|
1260
|
+
RETURNING id, entity_type, platform, platform_entity_id,
|
|
1261
|
+
title, content, metadata, created_at, updated_at,
|
|
1262
|
+
source_created_at, source_updated_at, synced_at, observed_at,
|
|
1263
|
+
retention_policy, retention_parent_id,
|
|
1264
|
+
cumulative_observation_duration_ms, bookmarked
|
|
1265
|
+
""",
|
|
1266
|
+
ids,
|
|
1267
|
+
)
|
|
1268
|
+
rows.extend(await cursor.fetchall())
|
|
1269
|
+
await conn.execute(
|
|
1270
|
+
f"DELETE FROM entities_fts WHERE id IN ({placeholders})", ids
|
|
1271
|
+
)
|
|
1272
|
+
if len(rows) != len(unique_ids):
|
|
1273
|
+
raise ValueError("One or more entities were not found")
|
|
1250
1274
|
await conn.execute("COMMIT")
|
|
1251
1275
|
except Exception:
|
|
1252
1276
|
await conn.execute("ROLLBACK")
|
|
1253
1277
|
raise
|
|
1254
|
-
|
|
1278
|
+
deleted_by_id = {str(row["id"]): _row_to_entity(row) for row in rows}
|
|
1279
|
+
return [deleted_by_id[entity_id] for entity_id in unique_ids]
|
|
1255
1280
|
|
|
1256
1281
|
# --- Read: entities ---
|
|
1257
1282
|
|
|
@@ -1269,6 +1294,7 @@ class SQLiteBackend(StorageBackend):
|
|
|
1269
1294
|
has_attachments: bool = False,
|
|
1270
1295
|
order_by: str | None = None,
|
|
1271
1296
|
content_limit: int | None = None,
|
|
1297
|
+
observed_since: datetime | None = None,
|
|
1272
1298
|
) -> list[EntityResult]:
|
|
1273
1299
|
"""Select entities by predicate, ranked by relevance or ordered by date.
|
|
1274
1300
|
|
|
@@ -1286,6 +1312,7 @@ class SQLiteBackend(StorageBackend):
|
|
|
1286
1312
|
since,
|
|
1287
1313
|
authored_by,
|
|
1288
1314
|
has_attachments,
|
|
1315
|
+
observed_since=observed_since,
|
|
1289
1316
|
)
|
|
1290
1317
|
|
|
1291
1318
|
if query_text is None or query_vec is None:
|
|
@@ -1540,6 +1567,30 @@ class SQLiteBackend(StorageBackend):
|
|
|
1540
1567
|
)
|
|
1541
1568
|
return _row_to_entity(row) if row else None
|
|
1542
1569
|
|
|
1570
|
+
async def get_existing_platform_entity_ids(
|
|
1571
|
+
self,
|
|
1572
|
+
platform: str,
|
|
1573
|
+
platform_entity_ids: list[str],
|
|
1574
|
+
) -> set[str]:
|
|
1575
|
+
if not platform_entity_ids:
|
|
1576
|
+
return set()
|
|
1577
|
+
|
|
1578
|
+
existing: set[str] = set()
|
|
1579
|
+
for offset in range(0, len(platform_entity_ids), _PLATFORM_ENTITY_ID_LOOKUP_CHUNK_SIZE):
|
|
1580
|
+
chunk = platform_entity_ids[
|
|
1581
|
+
offset : offset + _PLATFORM_ENTITY_ID_LOOKUP_CHUNK_SIZE
|
|
1582
|
+
]
|
|
1583
|
+
placeholders = ",".join("?" for _ in chunk)
|
|
1584
|
+
rows = await self._fetchall(
|
|
1585
|
+
f"""
|
|
1586
|
+
SELECT platform_entity_id FROM entities
|
|
1587
|
+
WHERE platform = ? AND platform_entity_id IN ({placeholders})
|
|
1588
|
+
""",
|
|
1589
|
+
[platform, *chunk],
|
|
1590
|
+
)
|
|
1591
|
+
existing.update(str(row["platform_entity_id"]) for row in rows)
|
|
1592
|
+
return existing
|
|
1593
|
+
|
|
1543
1594
|
async def list_entities(
|
|
1544
1595
|
self,
|
|
1545
1596
|
entity_types: list[str] | None,
|
|
@@ -1986,6 +2037,9 @@ class SQLiteBackend(StorageBackend):
|
|
|
1986
2037
|
[source, json.dumps(cursor), _now()],
|
|
1987
2038
|
)
|
|
1988
2039
|
|
|
2040
|
+
async def clear_cursor(self, source: str) -> None:
|
|
2041
|
+
await self._execute("DELETE FROM sync_state WHERE source = ?", [source])
|
|
2042
|
+
|
|
1989
2043
|
# --- Connector support ---
|
|
1990
2044
|
|
|
1991
2045
|
async def increment_observation_duration(
|
|
@@ -2123,6 +2177,26 @@ class SQLiteBackend(StorageBackend):
|
|
|
2123
2177
|
result[row["platform"]] = datetime.fromisoformat(val) if val else None
|
|
2124
2178
|
return result
|
|
2125
2179
|
|
|
2180
|
+
async def get_sources_last_synced_at(
|
|
2181
|
+
self, sources: list[str]
|
|
2182
|
+
) -> dict[str, datetime | None]:
|
|
2183
|
+
if not sources:
|
|
2184
|
+
return {}
|
|
2185
|
+
rows = await self._fetchall(
|
|
2186
|
+
"SELECT source, updated_at FROM sync_state"
|
|
2187
|
+
)
|
|
2188
|
+
result: dict[str, datetime | None] = dict.fromkeys(sources, None)
|
|
2189
|
+
for source in sources:
|
|
2190
|
+
matching = [
|
|
2191
|
+
row["updated_at"]
|
|
2192
|
+
for row in rows
|
|
2193
|
+
if row["source"] == source or row["source"].startswith(f"{source}:")
|
|
2194
|
+
]
|
|
2195
|
+
values = [datetime.fromisoformat(value) for value in matching if value]
|
|
2196
|
+
if values:
|
|
2197
|
+
result[source] = max(values)
|
|
2198
|
+
return result
|
|
2199
|
+
|
|
2126
2200
|
async def reset_synced_at(self, platform: str, platform_entity_id: str) -> None:
|
|
2127
2201
|
await self._execute(
|
|
2128
2202
|
"UPDATE entities SET synced_at = NULL WHERE platform = ? AND platform_entity_id = ?",
|
|
@@ -437,6 +437,13 @@ def connector_command(
|
|
|
437
437
|
try:
|
|
438
438
|
result = type(connector).run_cli_command(command_args)
|
|
439
439
|
effects = type(connector).command_effects(command_args, result)
|
|
440
|
+
if effects.reset_cursors:
|
|
441
|
+
from agentgraph.cli_query import run_graph_operation
|
|
442
|
+
from agentgraph.connectors.command_effects import execute_cursor_resets
|
|
443
|
+
|
|
444
|
+
result["reset_cursors"] = run_graph_operation(
|
|
445
|
+
lambda: execute_cursor_resets(effects)
|
|
446
|
+
)
|
|
440
447
|
if effects.delete_entities:
|
|
441
448
|
from agentgraph.cli_query import run_graph_operation
|
|
442
449
|
from agentgraph.connectors.command_effects import execute_deletions
|
|
@@ -484,7 +491,7 @@ def list_connectors(
|
|
|
484
491
|
False, "--verify", help="Live-check connector credentials with provider APIs"
|
|
485
492
|
),
|
|
486
493
|
) -> None:
|
|
487
|
-
"""List installed connectors and
|
|
494
|
+
"""List installed connectors, their resource entity types, and sync status."""
|
|
488
495
|
from agentgraph.connectors.registry import bootstrap, get_all_connectors
|
|
489
496
|
from agentgraph.connectors.status import connector_status_items
|
|
490
497
|
from agentgraph.core.runtime import backend_context
|
|
@@ -505,6 +512,7 @@ def list_connectors(
|
|
|
505
512
|
table = Table(title="Connectors", show_lines=True)
|
|
506
513
|
table.add_column("Connector", style="bold", no_wrap=True)
|
|
507
514
|
table.add_column("Description", ratio=1)
|
|
515
|
+
table.add_column("Types")
|
|
508
516
|
table.add_column("Auth")
|
|
509
517
|
table.add_column("Sync")
|
|
510
518
|
table.add_column("Last sync", no_wrap=True)
|
|
@@ -513,6 +521,11 @@ def list_connectors(
|
|
|
513
521
|
table.add_row(
|
|
514
522
|
str(item["source"]),
|
|
515
523
|
str(desc),
|
|
524
|
+
", ".join(
|
|
525
|
+
str(definition["name"])
|
|
526
|
+
for definition in cast(list[dict[str, object]], item["entity_types"])
|
|
527
|
+
)
|
|
528
|
+
or "-",
|
|
516
529
|
_connector_auth_label(item),
|
|
517
530
|
str(item["sync"]),
|
|
518
531
|
str(item["last_sync"]),
|
|
@@ -591,7 +604,7 @@ def search(
|
|
|
591
604
|
None,
|
|
592
605
|
"--since",
|
|
593
606
|
"-s",
|
|
594
|
-
help="Only results after this time: ISO timestamp or relative (12h, 30m, 2d)",
|
|
607
|
+
help="Only results updated at or after this time: ISO timestamp or relative (12h, 30m, 2d)",
|
|
595
608
|
),
|
|
596
609
|
mine: bool = typer.Option(False, "--mine", "-m", help="Only entities authored by me"),
|
|
597
610
|
has_attachments: bool = typer.Option(
|
|
@@ -613,6 +626,11 @@ def search(
|
|
|
613
626
|
0.03, "--min-score", help="Minimum relevance score (0–1); ignored without a query"
|
|
614
627
|
),
|
|
615
628
|
json: bool = typer.Option(False, "--json", help="Output as JSON"),
|
|
629
|
+
observed_since: str | None = typer.Option(
|
|
630
|
+
None,
|
|
631
|
+
"--observed-since",
|
|
632
|
+
help="Only results observed at or after this time: ISO timestamp or relative (12h, 30m, 2d)",
|
|
633
|
+
),
|
|
616
634
|
) -> None:
|
|
617
635
|
"""Search the knowledge graph, or list entities matching filters alone."""
|
|
618
636
|
from agentgraph.cli_query import cmd_search
|
|
@@ -623,6 +641,7 @@ def search(
|
|
|
623
641
|
platform=platform,
|
|
624
642
|
filters=dict(f.split("=", 1) for f in filter if "=" in f),
|
|
625
643
|
since=since,
|
|
644
|
+
observed_since=observed_since,
|
|
626
645
|
authored_by_me=mine,
|
|
627
646
|
has_attachments=has_attachments,
|
|
628
647
|
limit=limit,
|
|
@@ -725,13 +744,13 @@ def bookmark(
|
|
|
725
744
|
|
|
726
745
|
@app.command("delete")
|
|
727
746
|
def delete_cmd(
|
|
728
|
-
|
|
747
|
+
targets: list[str] = typer.Argument(..., help="Entity IDs, UUID prefixes, platform refs, or URLs"),
|
|
729
748
|
json: bool = typer.Option(False, "--json", help="Output as JSON"),
|
|
730
749
|
) -> None:
|
|
731
|
-
"""Delete
|
|
750
|
+
"""Delete one or more entities from the graph."""
|
|
732
751
|
from agentgraph.cli_query import cmd_delete
|
|
733
752
|
|
|
734
|
-
cmd_delete(
|
|
753
|
+
cmd_delete(targets=targets, as_json=json)
|
|
735
754
|
|
|
736
755
|
|
|
737
756
|
@app.command("unify-persons")
|
|
@@ -830,7 +849,7 @@ def mcp_config() -> None:
|
|
|
830
849
|
typer.echo()
|
|
831
850
|
typer.echo("Claude Code:")
|
|
832
851
|
typer.echo(" Register the server from your terminal:\n")
|
|
833
|
-
typer.echo(f" claude mcp add agentgraph -- {binary} mcp-serve")
|
|
852
|
+
typer.echo(f" claude mcp add --transport stdio --scope user agentgraph -- {binary} mcp-serve")
|
|
834
853
|
typer.echo()
|
|
835
854
|
typer.echo("Every client above runs the same server. Reads follow")
|
|
836
855
|
typer.echo("AGENTGRAPH_QUERY_TRANSPORT: by default the local server when one is")
|
|
@@ -874,7 +893,13 @@ def install_skill(
|
|
|
874
893
|
|
|
875
894
|
typer.echo(f"Installed AgentGraph skill '{result.skill}' to {result.destination}")
|
|
876
895
|
if result.claude_destination is not None:
|
|
877
|
-
|
|
896
|
+
if result.claude_linked:
|
|
897
|
+
typer.echo(f"Linked Claude skill to {result.claude_destination}")
|
|
898
|
+
else:
|
|
899
|
+
typer.echo(
|
|
900
|
+
f"Claude reads {result.claude_destination} already; "
|
|
901
|
+
"its skill directory is the same one."
|
|
902
|
+
)
|
|
878
903
|
|
|
879
904
|
|
|
880
905
|
@app.command()
|
|
@@ -109,6 +109,7 @@ def cmd_search(
|
|
|
109
109
|
authored_by_me: bool = False,
|
|
110
110
|
has_attachments: bool = False,
|
|
111
111
|
order_by: str | None = None,
|
|
112
|
+
observed_since: str | None = None,
|
|
112
113
|
) -> None:
|
|
113
114
|
# An empty or whitespace-only argument is an absent query, not a query for
|
|
114
115
|
# nothing: this keeps the limit default, the renderer, and the backend's
|
|
@@ -127,6 +128,7 @@ def cmd_search(
|
|
|
127
128
|
platform,
|
|
128
129
|
filters=filters,
|
|
129
130
|
since=since,
|
|
131
|
+
observed_since=observed_since,
|
|
130
132
|
authored_by_me=authored_by_me,
|
|
131
133
|
has_attachments=has_attachments,
|
|
132
134
|
order_by=order_by,
|
|
@@ -340,7 +342,22 @@ def cmd_bookmark(target: str, bookmarked: bool, as_json: bool) -> None:
|
|
|
340
342
|
console.print(f"[green]{action}:[/green] {label} [{result['id'][:8]}]")
|
|
341
343
|
|
|
342
344
|
|
|
343
|
-
def cmd_delete(
|
|
345
|
+
def cmd_delete(targets: list[str], as_json: bool) -> None:
|
|
346
|
+
if len(targets) == 1:
|
|
347
|
+
_cmd_delete_one(targets[0], as_json)
|
|
348
|
+
return
|
|
349
|
+
|
|
350
|
+
async def operation(client: QueryClient) -> dict[str, Any]:
|
|
351
|
+
return await client.delete_many(targets)
|
|
352
|
+
|
|
353
|
+
result = _run_with_client(operation)
|
|
354
|
+
if as_json:
|
|
355
|
+
console.print_json(json.dumps(result, default=str))
|
|
356
|
+
return
|
|
357
|
+
console.print(f"[green]Deleted {result['deleted_count']} entities.[/green]")
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _cmd_delete_one(target: str, as_json: bool) -> None:
|
|
344
361
|
async def operation(client: QueryClient) -> dict[str, Any]:
|
|
345
362
|
return await client.delete(target)
|
|
346
363
|
|