agentgraph-server 0.5.4__tar.gz → 0.6.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.
- {agentgraph_server-0.5.4/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/SKILL.md +1 -1
- {agentgraph_server-0.5.4/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/references/data-model.md +3 -1
- {agentgraph_server-0.5.4/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/references/operations.md +25 -4
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/.agents/skills/slack-auth/SKILL.md +4 -2
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/PKG-INFO +16 -15
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/README.md +3 -3
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/backend.py +75 -9
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/schema.sql +2 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/cli.py +118 -40
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/cli_query.py +19 -4
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/connectors/base.py +33 -1
- agentgraph_server-0.6.1/agentgraph/connectors/command_effects.py +69 -0
- agentgraph_server-0.6.1/agentgraph/connectors/feed.py +153 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/connectors/registry.py +8 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/core/storage.py +13 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/demo.py +3 -1
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/bookmark.py +23 -2
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/delete.py +14 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/query.py +5 -4
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/mcp/server.py +40 -8
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/server/meta_api.py +1 -1
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/server/observation.py +58 -2
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/server/static/viewer.html +23 -9
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/skills.py +6 -4
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/PKG-INFO +16 -15
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/SOURCES.txt +8 -4
- agentgraph_server-0.6.1/agentgraph_server.egg-info/requires.txt +38 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/pyproject.toml +15 -14
- agentgraph_server-0.6.1/tests/test_auth_status_methods.py +47 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_browse.py +7 -1
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_cli.py +346 -34
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_command_effects.py +58 -2
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_connectors.py +34 -1
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_docs_build.py +10 -5
- agentgraph_server-0.6.1/tests/test_entity_types.py +100 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_expiration.py +28 -0
- agentgraph_server-0.6.1/tests/test_feed_connectors.py +248 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_observation.py +54 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_query.py +152 -4
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_registry.py +14 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_release_metadata.py +1 -1
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_rss_connector.py +466 -25
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_schema.py +97 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_slack_auth_artifacts.py +2 -1
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_slack_oauth.py +32 -6
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_upsert.py +60 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_viewer_layout_contract.py +38 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_web_connector.py +210 -7
- agentgraph_server-0.5.4/agentgraph/connectors/command_effects.py +0 -18
- agentgraph_server-0.5.4/agentgraph_server.egg-info/requires.txt +0 -37
- {agentgraph_server-0.5.4/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/references/commands.md +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/LICENSE +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/__init__.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/auth/__init__.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/auth/credentials.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/backends/__init__.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/__init__.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/vector.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/cli_sync.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/config.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/connectors/__init__.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/connectors/status.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/core/__init__.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/core/context.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/core/runtime.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/demo_fixtures/reliable-webhooks.md +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/demo_fixtures/retry-guidance.md +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/__init__.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/download.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/embeddings.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/expiration.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/fetch.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/link.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/operations.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/person.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/graph/upsert.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/logging.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/mcp/__init__.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/perf.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/server/__init__.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/server/app.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/server/cli_api.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/server/router.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/server/sync.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph/server/sync_api.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/dependency_links.txt +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/entry_points.txt +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/top_level.txt +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/setup.cfg +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_auth.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_benchmarks.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_config.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_discord_attachments.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_embeddings.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_fetch.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_graph_operations.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_launch_demo.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_logging.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_router.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_server_imports.py +0 -0
- {agentgraph_server-0.5.4 → agentgraph_server-0.6.1}/tests/test_sync.py +0 -0
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
| `Message` | Chat messages. Chat images and uploads are stored in `metadata.attachments`. |
|
|
12
12
|
| `Person` | Source identities and confirmed cross-source identity merges. |
|
|
13
13
|
| `Spreadsheet` | Google Sheets and other spreadsheet resources. |
|
|
14
|
+
| `Task` | Tracked work items such as Jira issues. Issue key, status, and assignee live in `metadata`. |
|
|
15
|
+
| `Video` | Recorded videos such as Looms. The transcript is indexed as content and `metadata.web_url` links to the video. |
|
|
14
16
|
|
|
15
|
-
The valid core model does not currently include `
|
|
17
|
+
The valid core model does not currently include `Project`.
|
|
16
18
|
|
|
17
19
|
## Relationships
|
|
18
20
|
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
## Availability and authentication
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
agentgraph connectors [--verify] [--json]
|
|
6
|
+
agentgraph list-connectors [--verify] [--json]
|
|
7
7
|
agentgraph auth [--verify] [--json] status
|
|
8
8
|
agentgraph auth <provider> [--add] [--account <account-id>]
|
|
9
9
|
agentgraph auth remove <provider> [--account <account-id>] [--json]
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
Use `agentgraph connectors --json` to discover installed connector source names,
|
|
12
|
+
Use `agentgraph list-connectors --json` to discover installed connector source names,
|
|
13
13
|
valid platform values, URL ownership, polling delegation, and sync state. Do not rely
|
|
14
14
|
on a hardcoded platform list. Use `--verify` only when a live provider check is
|
|
15
|
-
needed.
|
|
15
|
+
needed. Without `--json`, the CLI renders the same status as an operator-facing table.
|
|
16
16
|
|
|
17
17
|
MCP equivalents are `list_connectors_tool`, `list_auth_providers_tool`,
|
|
18
18
|
`authenticate_provider_tool`, and `remove_auth_provider_tool`.
|
|
@@ -45,6 +45,22 @@ agentgraph connector gmail ingest [--account <account-id>] [--json]
|
|
|
45
45
|
The MCP form is `run_connector_command_tool("gmail", ["ingest", ...])`. There is no
|
|
46
46
|
top-level `agentgraph ingest` command or `ingest_connector_tool`.
|
|
47
47
|
|
|
48
|
+
For an oversized HTML page, request a one-off web fetch that removes style, script,
|
|
49
|
+
noscript, and comment blocks before applying the response-size limit:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
agentgraph connector web fetch <url> --compact [--json]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The MCP form is `run_connector_command_tool("web", ["fetch", "<url>", "--compact"])`.
|
|
56
|
+
Compaction applies only to that command; ordinary fetches, bookmarks, observations, and
|
|
57
|
+
RSS article hydration retain their default behavior.
|
|
58
|
+
|
|
59
|
+
Configure browser observation rules with `agentgraph connector web observe <url-or-prefix>`
|
|
60
|
+
and remove them with `agentgraph connector web observe <url-or-prefix> --remove`. The MCP
|
|
61
|
+
forms are `run_connector_command_tool("web", ["observe", "<url-or-prefix>"])` and
|
|
62
|
+
`run_connector_command_tool("web", ["observe", "<url-or-prefix>", "--remove"])`.
|
|
63
|
+
|
|
48
64
|
## Files and retained context
|
|
49
65
|
|
|
50
66
|
```bash
|
|
@@ -71,7 +87,12 @@ commands, request permission to contact the configured localhost server and retr
|
|
|
71
87
|
```bash
|
|
72
88
|
agentgraph mcp-config
|
|
73
89
|
agentgraph mcp-serve
|
|
74
|
-
agentgraph install-skill [
|
|
90
|
+
agentgraph install-skill [agentgraph] [--target user|project] [--no-claude] [--force] [--json]
|
|
75
91
|
```
|
|
76
92
|
|
|
93
|
+
`agentgraph mcp-config` prints local stdio setup for ChatGPT Desktop Work Mode and
|
|
94
|
+
Claude Desktop. In ChatGPT Desktop, enter the printed executable in **Command to
|
|
95
|
+
launch** and `mcp-serve` in **Arguments**; for Claude Desktop, add the printed JSON
|
|
96
|
+
to its MCP configuration file.
|
|
97
|
+
|
|
77
98
|
The skill installer is available only through the `agentgraph install-skill` CLI command.
|
|
@@ -28,8 +28,10 @@ permission in the target workspace.
|
|
|
28
28
|
Admins create AgentGraph at `https://api.slack.com/apps` with **Create New App →
|
|
29
29
|
From an app manifest**, select the target workspace, paste the manifest printed by
|
|
30
30
|
AgentGraph into Slack's **JSON** tab, approve it, and enter its Client ID. The
|
|
31
|
-
manifest includes the
|
|
32
|
-
|
|
31
|
+
manifest includes the callback, which defaults to `http://localhost:8766/slack/oauth/callback`; set
|
|
32
|
+
`AGENTGRAPH_SLACK_OAUTH_CALLBACK_PORT` before setup to use another local port. The
|
|
33
|
+
manifest printed by AgentGraph uses that port, which must match the Slack app's
|
|
34
|
+
registered redirect URL.
|
|
33
35
|
|
|
34
36
|
Non-admins choose either **Enter a Client ID provided by a Slack admin** or **Set up
|
|
35
37
|
the AgentGraph Slack App**. The setup path checks whether the target workspace
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentgraph-server
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
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.7,>=0.5.4
|
|
10
10
|
Requires-Dist: apscheduler>=3.11.2
|
|
11
11
|
Requires-Dist: click<8.4
|
|
12
12
|
Requires-Dist: fastapi>=0.135.2
|
|
@@ -16,26 +16,27 @@ Requires-Dist: mcp>=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
|
|
19
|
+
Requires-Dist: questionary>=2.1.1
|
|
19
20
|
Requires-Dist: rich>=14.3.3
|
|
20
21
|
Requires-Dist: sqlite-vec>=0.1.9
|
|
21
22
|
Requires-Dist: typer>=0.24.1
|
|
22
23
|
Requires-Dist: uvicorn>=0.42.0
|
|
23
24
|
Provides-Extra: google
|
|
24
|
-
Requires-Dist: agentgraph-connector-google<0.
|
|
25
|
+
Requires-Dist: agentgraph-connector-google<0.7,>=0.5.4; extra == "google"
|
|
25
26
|
Provides-Extra: slack
|
|
26
|
-
Requires-Dist: agentgraph-connector-slack<0.
|
|
27
|
+
Requires-Dist: agentgraph-connector-slack<0.7,>=0.5.4; extra == "slack"
|
|
27
28
|
Provides-Extra: discord
|
|
28
|
-
Requires-Dist: agentgraph-connector-discord<0.
|
|
29
|
+
Requires-Dist: agentgraph-connector-discord<0.7,>=0.5.4; extra == "discord"
|
|
29
30
|
Provides-Extra: rss
|
|
30
|
-
Requires-Dist: agentgraph-connector-rss<0.
|
|
31
|
+
Requires-Dist: agentgraph-connector-rss<0.7,>=0.5.4; extra == "rss"
|
|
31
32
|
Provides-Extra: web
|
|
32
|
-
Requires-Dist: agentgraph-connector-web<0.
|
|
33
|
+
Requires-Dist: agentgraph-connector-web<0.7,>=0.5.4; extra == "web"
|
|
33
34
|
Provides-Extra: all
|
|
34
|
-
Requires-Dist: agentgraph-connector-web<0.
|
|
35
|
-
Requires-Dist: agentgraph-connector-google<0.
|
|
36
|
-
Requires-Dist: agentgraph-connector-slack<0.
|
|
37
|
-
Requires-Dist: agentgraph-connector-discord<0.
|
|
38
|
-
Requires-Dist: agentgraph-connector-rss<0.
|
|
35
|
+
Requires-Dist: agentgraph-connector-web<0.7,>=0.5.4; extra == "all"
|
|
36
|
+
Requires-Dist: agentgraph-connector-google<0.7,>=0.5.4; extra == "all"
|
|
37
|
+
Requires-Dist: agentgraph-connector-slack<0.7,>=0.5.4; extra == "all"
|
|
38
|
+
Requires-Dist: agentgraph-connector-discord<0.7,>=0.5.4; extra == "all"
|
|
39
|
+
Requires-Dist: agentgraph-connector-rss<0.7,>=0.5.4; extra == "all"
|
|
39
40
|
Dynamic: license-file
|
|
40
41
|
|
|
41
42
|
# AgentGraph
|
|
@@ -46,7 +47,7 @@ AgentGraph is a local-first, self-hosted CLI and MCP server that turns the sourc
|
|
|
46
47
|
|
|
47
48
|
> **AgentGraph stores context; your agent reasons over it.** It is not an agent, chatbot, hosted graph service, or replacement for the tools you already use.
|
|
48
49
|
|
|
49
|
-
[See the demo](docs-src/demo.md) · [Install](docs-src/install.md) · [Documentation](https://
|
|
50
|
+
[See the demo](docs-src/demo.md) · [Install](docs-src/install.md) · [Documentation](https://agentgraph.simonwa.de/) · [Chrome extension](https://chromewebstore.google.com/detail/agentgraph-extension/iilkfclglabllelhjacijldknapbhidi)
|
|
50
51
|
|
|
51
52
|
<picture>
|
|
52
53
|
<source media="(prefers-color-scheme: dark)" srcset="docs-src/assets/diagrams/architecture-overview-dark.svg">
|
|
@@ -110,7 +111,7 @@ agentgraph serve
|
|
|
110
111
|
Install the [AgentGraph Chrome extension](https://chromewebstore.google.com/detail/agentgraph-extension/iilkfclglabllelhjacijldknapbhidi), browse a supported resource, then verify that context landed:
|
|
111
112
|
|
|
112
113
|
```bash
|
|
113
|
-
agentgraph connectors
|
|
114
|
+
agentgraph list-connectors
|
|
114
115
|
agentgraph search "project kickoff notes"
|
|
115
116
|
agentgraph traverse <entity-id> --depth 2
|
|
116
117
|
```
|
|
@@ -121,7 +122,7 @@ Connect the agent you already use:
|
|
|
121
122
|
agentgraph mcp-config
|
|
122
123
|
```
|
|
123
124
|
|
|
124
|
-
Use the printed
|
|
125
|
+
Use the printed instructions to connect ChatGPT Desktop Work Mode or Claude Desktop to the local stdio server. See [Install](docs-src/install.md) for the complete setup path.
|
|
125
126
|
|
|
126
127
|
## What AgentGraph is and is not
|
|
127
128
|
|
|
@@ -6,7 +6,7 @@ AgentGraph is a local-first, self-hosted CLI and MCP server that turns the sourc
|
|
|
6
6
|
|
|
7
7
|
> **AgentGraph stores context; your agent reasons over it.** It is not an agent, chatbot, hosted graph service, or replacement for the tools you already use.
|
|
8
8
|
|
|
9
|
-
[See the demo](docs-src/demo.md) · [Install](docs-src/install.md) · [Documentation](https://
|
|
9
|
+
[See the demo](docs-src/demo.md) · [Install](docs-src/install.md) · [Documentation](https://agentgraph.simonwa.de/) · [Chrome extension](https://chromewebstore.google.com/detail/agentgraph-extension/iilkfclglabllelhjacijldknapbhidi)
|
|
10
10
|
|
|
11
11
|
<picture>
|
|
12
12
|
<source media="(prefers-color-scheme: dark)" srcset="docs-src/assets/diagrams/architecture-overview-dark.svg">
|
|
@@ -70,7 +70,7 @@ agentgraph serve
|
|
|
70
70
|
Install the [AgentGraph Chrome extension](https://chromewebstore.google.com/detail/agentgraph-extension/iilkfclglabllelhjacijldknapbhidi), browse a supported resource, then verify that context landed:
|
|
71
71
|
|
|
72
72
|
```bash
|
|
73
|
-
agentgraph connectors
|
|
73
|
+
agentgraph list-connectors
|
|
74
74
|
agentgraph search "project kickoff notes"
|
|
75
75
|
agentgraph traverse <entity-id> --depth 2
|
|
76
76
|
```
|
|
@@ -81,7 +81,7 @@ Connect the agent you already use:
|
|
|
81
81
|
agentgraph mcp-config
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
Use the printed
|
|
84
|
+
Use the printed instructions to connect ChatGPT Desktop Work Mode or Claude Desktop to the local stdio server. See [Install](docs-src/install.md) for the complete setup path.
|
|
85
85
|
|
|
86
86
|
## What AgentGraph is and is not
|
|
87
87
|
|
|
@@ -64,7 +64,7 @@ _LIST_PAGE_ORDER_BY = {
|
|
|
64
64
|
_COLUMN_FILTERS = {"platform", "platform_entity_id", "entity_type"}
|
|
65
65
|
_FTS_DELETE_CHUNK_SIZE = 500
|
|
66
66
|
_BUSY_TIMEOUT_MS = 5_000
|
|
67
|
-
_SCHEMA_VERSION =
|
|
67
|
+
_SCHEMA_VERSION = 4
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
def _now() -> str:
|
|
@@ -208,7 +208,9 @@ class SQLiteBackend(StorageBackend):
|
|
|
208
208
|
async def _run_migrations(self) -> None:
|
|
209
209
|
conn = self._conn_or_raise()
|
|
210
210
|
cursor = await conn.execute("PRAGMA table_info(entities)")
|
|
211
|
-
|
|
211
|
+
table_info = await cursor.fetchall()
|
|
212
|
+
entity_columns = {str(row["name"]): bool(row["notnull"]) for row in table_info}
|
|
213
|
+
entity_defaults = {str(row["name"]): row["dflt_value"] for row in table_info}
|
|
212
214
|
columns = set(entity_columns)
|
|
213
215
|
if "cumulative_dwell_ms" in columns:
|
|
214
216
|
await conn.execute(
|
|
@@ -234,6 +236,8 @@ class SQLiteBackend(StorageBackend):
|
|
|
234
236
|
"last_accessed" in columns
|
|
235
237
|
or not entity_columns.get("created_at", False)
|
|
236
238
|
or not entity_columns.get("updated_at", False)
|
|
239
|
+
or entity_defaults.get("created_at") is None
|
|
240
|
+
or entity_defaults.get("updated_at") is None
|
|
237
241
|
or entity_columns.get("observed_at", False)
|
|
238
242
|
or "source_created_at" not in columns
|
|
239
243
|
or "source_updated_at" not in columns
|
|
@@ -287,6 +291,10 @@ class SQLiteBackend(StorageBackend):
|
|
|
287
291
|
await conn.execute(
|
|
288
292
|
"CREATE INDEX IF NOT EXISTS idx_entities_platform_type_observed_at ON entities(platform, entity_type, observed_at DESC)"
|
|
289
293
|
)
|
|
294
|
+
await conn.execute(
|
|
295
|
+
"CREATE INDEX IF NOT EXISTS idx_entities_platform_type_feed_updated "
|
|
296
|
+
"ON entities(platform, entity_type, json_extract(metadata, '$.feed_url'), updated_at DESC)"
|
|
297
|
+
)
|
|
290
298
|
|
|
291
299
|
async def _retention_policy_needs_migration(self) -> bool:
|
|
292
300
|
"""Return whether the entity policy constraint lacks persistent support."""
|
|
@@ -344,7 +352,8 @@ class SQLiteBackend(StorageBackend):
|
|
|
344
352
|
observed = "NULL"
|
|
345
353
|
if "observed_at" in columns and "cumulative_observation_duration_ms" in columns:
|
|
346
354
|
observed = (
|
|
347
|
-
"CASE WHEN entity_type IN
|
|
355
|
+
"CASE WHEN entity_type IN "
|
|
356
|
+
"('Channel', 'Document', 'Email', 'Folder', 'Spreadsheet', 'Task', 'Video') "
|
|
348
357
|
"AND cumulative_observation_duration_ms > 0 THEN observed_at ELSE NULL END"
|
|
349
358
|
)
|
|
350
359
|
observed = (
|
|
@@ -370,8 +379,8 @@ class SQLiteBackend(StorageBackend):
|
|
|
370
379
|
content TEXT,
|
|
371
380
|
content_embedding BLOB,
|
|
372
381
|
metadata TEXT NOT NULL DEFAULT '{}',
|
|
373
|
-
created_at TEXT NOT NULL,
|
|
374
|
-
updated_at TEXT NOT NULL,
|
|
382
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
|
|
383
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
|
|
375
384
|
source_created_at TEXT,
|
|
376
385
|
source_updated_at TEXT,
|
|
377
386
|
synced_at TEXT,
|
|
@@ -660,8 +669,8 @@ class SQLiteBackend(StorageBackend):
|
|
|
660
669
|
"""
|
|
661
670
|
INSERT INTO entities
|
|
662
671
|
(id, entity_type, platform, platform_entity_id, title, metadata,
|
|
663
|
-
retention_policy, retention_parent_id)
|
|
664
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
672
|
+
created_at, updated_at, retention_policy, retention_parent_id)
|
|
673
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
665
674
|
ON CONFLICT (platform, platform_entity_id) DO UPDATE SET
|
|
666
675
|
entity_type = entities.entity_type
|
|
667
676
|
RETURNING id
|
|
@@ -673,6 +682,8 @@ class SQLiteBackend(StorageBackend):
|
|
|
673
682
|
e.platform_entity_id,
|
|
674
683
|
e.title,
|
|
675
684
|
json.dumps(dict(e.metadata)),
|
|
685
|
+
now,
|
|
686
|
+
now,
|
|
676
687
|
e.retention_policy,
|
|
677
688
|
parent_id,
|
|
678
689
|
],
|
|
@@ -756,8 +767,8 @@ class SQLiteBackend(StorageBackend):
|
|
|
756
767
|
INSERT INTO entities
|
|
757
768
|
(id, entity_type, platform, platform_entity_id, title, content,
|
|
758
769
|
content_embedding, metadata, source_created_at, source_updated_at,
|
|
759
|
-
synced_at, retention_policy, retention_parent_id)
|
|
760
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
770
|
+
synced_at, created_at, updated_at, retention_policy, retention_parent_id)
|
|
771
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
761
772
|
ON CONFLICT (platform, platform_entity_id) DO UPDATE SET
|
|
762
773
|
entity_type = CASE WHEN entities.entity_type = 'Document' THEN EXCLUDED.entity_type ELSE entities.entity_type END,
|
|
763
774
|
title = COALESCE(EXCLUDED.title, entities.title),
|
|
@@ -784,6 +795,8 @@ class SQLiteBackend(StorageBackend):
|
|
|
784
795
|
source_created,
|
|
785
796
|
source_updated,
|
|
786
797
|
now,
|
|
798
|
+
now,
|
|
799
|
+
local_updated,
|
|
787
800
|
e.retention_policy,
|
|
788
801
|
parent_id,
|
|
789
802
|
local_updated,
|
|
@@ -1446,6 +1459,59 @@ class SQLiteBackend(StorageBackend):
|
|
|
1446
1459
|
)
|
|
1447
1460
|
return [_row_to_entity(row) for row in rows]
|
|
1448
1461
|
|
|
1462
|
+
async def list_recent_metadata_by_group(
|
|
1463
|
+
self,
|
|
1464
|
+
entity_type: str,
|
|
1465
|
+
filters: dict[str, str],
|
|
1466
|
+
group_metadata_key: str,
|
|
1467
|
+
group_values: list[str],
|
|
1468
|
+
per_group_limit: int,
|
|
1469
|
+
order_by: str,
|
|
1470
|
+
) -> list[dict[str, Any]]:
|
|
1471
|
+
if not group_values or per_group_limit <= 0:
|
|
1472
|
+
return []
|
|
1473
|
+
if order_by not in _VALID_ORDER_BY:
|
|
1474
|
+
order_by = "updated_at"
|
|
1475
|
+
|
|
1476
|
+
params: list[Any] = [entity_type]
|
|
1477
|
+
clauses: list[str] = []
|
|
1478
|
+
for key, value in filters.items():
|
|
1479
|
+
if key in _COLUMN_FILTERS:
|
|
1480
|
+
clauses.append(f"e.{key} = ?")
|
|
1481
|
+
else:
|
|
1482
|
+
clauses.append(f"json_extract(e.metadata, '$.{key}') = ?")
|
|
1483
|
+
params.append(value)
|
|
1484
|
+
|
|
1485
|
+
group_path = f"$.{group_metadata_key}"
|
|
1486
|
+
value_placeholders = ", ".join("?" for _ in group_values)
|
|
1487
|
+
clauses.append(f"json_extract(e.metadata, ?) IN ({value_placeholders})")
|
|
1488
|
+
where_extra = " AND ".join(clauses)
|
|
1489
|
+
|
|
1490
|
+
with timed(
|
|
1491
|
+
"sqlite.list_recent_metadata_by_group",
|
|
1492
|
+
entity_type=entity_type,
|
|
1493
|
+
per_group_limit=per_group_limit,
|
|
1494
|
+
group_count=len(group_values),
|
|
1495
|
+
):
|
|
1496
|
+
rows = await self._fetchall(
|
|
1497
|
+
f"""
|
|
1498
|
+
SELECT metadata FROM (
|
|
1499
|
+
SELECT e.metadata,
|
|
1500
|
+
json_extract(e.metadata, ?) AS group_value,
|
|
1501
|
+
ROW_NUMBER() OVER (
|
|
1502
|
+
PARTITION BY json_extract(e.metadata, ?)
|
|
1503
|
+
ORDER BY e.{order_by} DESC
|
|
1504
|
+
) AS group_rank
|
|
1505
|
+
FROM entities e
|
|
1506
|
+
WHERE e.entity_type = ? AND {where_extra}
|
|
1507
|
+
)
|
|
1508
|
+
WHERE group_rank <= ?
|
|
1509
|
+
ORDER BY group_value, group_rank
|
|
1510
|
+
""",
|
|
1511
|
+
[group_path, group_path, *params, group_path, *group_values, per_group_limit],
|
|
1512
|
+
)
|
|
1513
|
+
return [json.loads(row["metadata"]) if row["metadata"] else {} for row in rows]
|
|
1514
|
+
|
|
1449
1515
|
# --- Read: edges ---
|
|
1450
1516
|
|
|
1451
1517
|
async def get_edges(
|
|
@@ -68,6 +68,8 @@ CREATE INDEX IF NOT EXISTS idx_entities_type_created_at ON entities(entity_type,
|
|
|
68
68
|
CREATE INDEX IF NOT EXISTS idx_entities_type_updated_at ON entities(entity_type, updated_at DESC);
|
|
69
69
|
CREATE INDEX IF NOT EXISTS idx_entities_platform_observed_at_id ON entities(platform, observed_at DESC, id ASC);
|
|
70
70
|
CREATE INDEX IF NOT EXISTS idx_entities_platform_type_observed_at ON entities(platform, entity_type, observed_at DESC);
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_entities_platform_type_feed_updated
|
|
72
|
+
ON entities(platform, entity_type, json_extract(metadata, '$.feed_url'), updated_at DESC);
|
|
71
73
|
|
|
72
74
|
-- Edge indexes
|
|
73
75
|
CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_entity_id);
|
|
@@ -4,11 +4,18 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import json as _json
|
|
7
|
+
import urllib.error
|
|
8
|
+
import urllib.request
|
|
7
9
|
from collections.abc import Iterator
|
|
8
10
|
from contextlib import contextmanager
|
|
11
|
+
from importlib.metadata import PackageNotFoundError
|
|
12
|
+
from importlib.metadata import version as package_version
|
|
9
13
|
from typing import TYPE_CHECKING, cast
|
|
10
14
|
|
|
15
|
+
import questionary # type: ignore[import-untyped]
|
|
11
16
|
import typer
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.table import Table
|
|
12
19
|
|
|
13
20
|
if TYPE_CHECKING:
|
|
14
21
|
from agentgraph.connectors.base import BaseConnector
|
|
@@ -18,6 +25,34 @@ app = typer.Typer(
|
|
|
18
25
|
help="Local knowledge graph for AI agents.",
|
|
19
26
|
no_args_is_help=True,
|
|
20
27
|
)
|
|
28
|
+
console = Console()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _version_callback(value: bool) -> None:
|
|
32
|
+
if not value:
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
current = package_version("agentgraph-server")
|
|
37
|
+
except PackageNotFoundError:
|
|
38
|
+
current = "unknown"
|
|
39
|
+
typer.echo(f"agentgraph {current}")
|
|
40
|
+
raise typer.Exit()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@app.callback()
|
|
44
|
+
def main(
|
|
45
|
+
version: bool = typer.Option(
|
|
46
|
+
False,
|
|
47
|
+
"--version",
|
|
48
|
+
callback=_version_callback,
|
|
49
|
+
is_eager=True,
|
|
50
|
+
help="Show the installed version and exit.",
|
|
51
|
+
),
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Configure global CLI options."""
|
|
54
|
+
|
|
55
|
+
|
|
21
56
|
demo_app = typer.Typer(
|
|
22
57
|
help="Create reproducible local demo graphs.",
|
|
23
58
|
no_args_is_help=True,
|
|
@@ -50,6 +85,34 @@ def _status_label(status: str) -> str:
|
|
|
50
85
|
)
|
|
51
86
|
|
|
52
87
|
|
|
88
|
+
def _connector_auth_label(item: dict[str, object]) -> str:
|
|
89
|
+
"""Build the authentication cell for a connector status row."""
|
|
90
|
+
status = item["auth_status"]
|
|
91
|
+
if status is None:
|
|
92
|
+
return ""
|
|
93
|
+
|
|
94
|
+
status_label = str(status)
|
|
95
|
+
auth = _status_label(status_label)
|
|
96
|
+
detail = item["auth_detail"]
|
|
97
|
+
if detail:
|
|
98
|
+
auth = f"{auth} ({detail})" if status_label != "ok" else f"{auth} as {detail}"
|
|
99
|
+
return f"{auth} via {item['auth_provider']}"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _server_is_running() -> bool:
|
|
103
|
+
"""Return whether the configured AgentGraph server responds to health checks."""
|
|
104
|
+
from agentgraph.config import get_settings
|
|
105
|
+
|
|
106
|
+
settings = get_settings()
|
|
107
|
+
host = "127.0.0.1" if settings.server_host in ("", "0.0.0.0", "::") else settings.server_host
|
|
108
|
+
url = f"http://{host}:{settings.server_port}/health"
|
|
109
|
+
try:
|
|
110
|
+
with urllib.request.urlopen(url, timeout=1) as response:
|
|
111
|
+
return response.status == 200
|
|
112
|
+
except (OSError, urllib.error.URLError):
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
|
|
53
116
|
@demo_app.command("add")
|
|
54
117
|
def add_demo_command(
|
|
55
118
|
json: bool = typer.Option(False, "--json", help="Output as JSON"),
|
|
@@ -322,7 +385,7 @@ def connector_command(
|
|
|
322
385
|
help: bool = typer.Option(False, "--help", help="Show this message and exit."),
|
|
323
386
|
) -> None:
|
|
324
387
|
"""Run a connector-owned command."""
|
|
325
|
-
from agentgraph.connectors.registry import bootstrap, get_connector
|
|
388
|
+
from agentgraph.connectors.registry import bootstrap, get_connector, get_connector_load_error
|
|
326
389
|
|
|
327
390
|
if source is None:
|
|
328
391
|
from typer.rich_utils import rich_format_help
|
|
@@ -333,6 +396,10 @@ def connector_command(
|
|
|
333
396
|
bootstrap()
|
|
334
397
|
connector = get_connector(source)
|
|
335
398
|
if connector is None:
|
|
399
|
+
if error := get_connector_load_error(source):
|
|
400
|
+
typer.echo(f"Failed to load connector '{source}': {error}", err=True)
|
|
401
|
+
typer.echo("Reinstall AgentGraph and its connector dependencies, then retry.", err=True)
|
|
402
|
+
raise typer.Exit(code=1)
|
|
336
403
|
typer.echo(f"Unknown connector '{source}'", err=True)
|
|
337
404
|
raise typer.Exit(code=1)
|
|
338
405
|
|
|
@@ -349,6 +416,17 @@ def connector_command(
|
|
|
349
416
|
from agentgraph.connectors.command_effects import execute_deletions
|
|
350
417
|
|
|
351
418
|
result["deleted_entities"] = run_graph_operation(lambda: execute_deletions(effects))
|
|
419
|
+
if effects.fetch_references:
|
|
420
|
+
from agentgraph.cli_query import run_graph_operation
|
|
421
|
+
from agentgraph.connectors.command_effects import (
|
|
422
|
+
execute_fetches,
|
|
423
|
+
fetch_effect_error_hint,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
result["fetched"] = run_graph_operation(
|
|
427
|
+
lambda: execute_fetches(effects),
|
|
428
|
+
error_hint=lambda error: fetch_effect_error_hint(effects, error, "cli"),
|
|
429
|
+
)
|
|
352
430
|
if effects.poll:
|
|
353
431
|
from agentgraph.cli_sync import queue_connector_poll
|
|
354
432
|
|
|
@@ -373,8 +451,8 @@ def connector_command(
|
|
|
373
451
|
typer.echo(type(connector).format_cli_result(result))
|
|
374
452
|
|
|
375
453
|
|
|
376
|
-
@app.command()
|
|
377
|
-
def
|
|
454
|
+
@app.command(name="list-connectors")
|
|
455
|
+
def list_connectors(
|
|
378
456
|
json: bool = typer.Option(False, "--json", help="Output as JSON"),
|
|
379
457
|
verify: bool = typer.Option(
|
|
380
458
|
False, "--verify", help="Live-check connector credentials with provider APIs"
|
|
@@ -398,23 +476,22 @@ def connectors(
|
|
|
398
476
|
typer.echo(_json.dumps(items, indent=2))
|
|
399
477
|
return
|
|
400
478
|
|
|
479
|
+
table = Table(title="Connectors", show_lines=True)
|
|
480
|
+
table.add_column("Connector", style="bold", no_wrap=True)
|
|
481
|
+
table.add_column("Description", ratio=1)
|
|
482
|
+
table.add_column("Auth")
|
|
483
|
+
table.add_column("Sync")
|
|
484
|
+
table.add_column("Last sync", no_wrap=True)
|
|
401
485
|
for item in items:
|
|
402
|
-
sync = str(item["sync"])
|
|
403
|
-
last_sync = str(item["last_sync"])
|
|
404
486
|
desc = item["description"] or item["source"]
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
detail = item["auth_detail"]
|
|
412
|
-
auth = _status_label(status_label)
|
|
413
|
-
if detail:
|
|
414
|
-
auth = f"{auth} ({detail})" if status_label != "ok" else f"{auth} as {detail}"
|
|
415
|
-
typer.echo(
|
|
416
|
-
f" {'':<12} auth: {auth} via {item['auth_provider']} | sync: {sync} | last sync: {last_sync}"
|
|
487
|
+
table.add_row(
|
|
488
|
+
str(item["source"]),
|
|
489
|
+
str(desc),
|
|
490
|
+
_connector_auth_label(item),
|
|
491
|
+
str(item["sync"]),
|
|
492
|
+
str(item["last_sync"]),
|
|
417
493
|
)
|
|
494
|
+
console.print(table)
|
|
418
495
|
|
|
419
496
|
|
|
420
497
|
@app.command()
|
|
@@ -430,6 +507,7 @@ def serve(
|
|
|
430
507
|
settings = get_settings()
|
|
431
508
|
configure_logging(settings.log_level, settings.log_file)
|
|
432
509
|
typer.echo(f"AgentGraph config directory: {get_config_paths()[0]}")
|
|
510
|
+
typer.echo(f"AgentGraph log file: {settings.log_file.expanduser()}")
|
|
433
511
|
uvicorn.run(
|
|
434
512
|
"agentgraph.server.app:app",
|
|
435
513
|
host=settings.server_host,
|
|
@@ -595,8 +673,15 @@ def onboard() -> None:
|
|
|
595
673
|
if label not in seen:
|
|
596
674
|
seen[label] = connector
|
|
597
675
|
|
|
598
|
-
|
|
599
|
-
|
|
676
|
+
# Only connectors with an explicit onboarding prompt own an interactive setup flow.
|
|
677
|
+
# Generic connectors such as Web are configured through connector commands instead.
|
|
678
|
+
steps = [
|
|
679
|
+
(label, connector)
|
|
680
|
+
for label, connector in seen.items()
|
|
681
|
+
if getattr(connector, "onboard_prompt", None)
|
|
682
|
+
]
|
|
683
|
+
steps.sort(key=lambda item: getattr(item[1], "onboard_last", False))
|
|
684
|
+
total = len(steps) + 1
|
|
600
685
|
|
|
601
686
|
typer.echo("=== AgentGraph Setup ===\n")
|
|
602
687
|
|
|
@@ -604,18 +689,19 @@ def onboard() -> None:
|
|
|
604
689
|
prompt: str = getattr(connector, "onboard_prompt", None) or f"Set up {label}?"
|
|
605
690
|
description: str = getattr(connector, "auth_description", None) or label.title()
|
|
606
691
|
typer.echo(f"Step {i}/{total}: {description}")
|
|
607
|
-
if
|
|
692
|
+
if questionary.confirm(f" {prompt}", default=True).ask():
|
|
608
693
|
type(connector).run_auth_flow()
|
|
609
694
|
else:
|
|
610
695
|
typer.echo(" Skipped.")
|
|
611
696
|
if i < total:
|
|
612
697
|
typer.echo()
|
|
613
698
|
|
|
699
|
+
typer.echo(f"\nStep {total}/{total}: Install the AgentGraph Chrome Extension")
|
|
614
700
|
typer.echo(
|
|
615
|
-
"\nInstall the AgentGraph Chrome Extension: "
|
|
616
701
|
"https://chromewebstore.google.com/detail/agentgraph-extension/iilkfclglabllelhjacijldknapbhidi"
|
|
617
702
|
)
|
|
618
|
-
|
|
703
|
+
if not _server_is_running():
|
|
704
|
+
typer.echo("Run `agentgraph serve` to start the server.")
|
|
619
705
|
|
|
620
706
|
|
|
621
707
|
@app.command()
|
|
@@ -635,30 +721,22 @@ def mcp_config() -> None:
|
|
|
635
721
|
}
|
|
636
722
|
}
|
|
637
723
|
|
|
638
|
-
typer.echo("\
|
|
639
|
-
typer.echo("
|
|
640
|
-
typer.echo("
|
|
641
|
-
typer.echo(
|
|
642
|
-
typer.echo()
|
|
643
|
-
typer.echo("
|
|
644
|
-
typer.echo("Run a streamable HTTP MCP server:")
|
|
645
|
-
typer.echo(f" {binary} mcp-serve --transport streamable-http --port 8808")
|
|
646
|
-
typer.echo("Local endpoint:")
|
|
647
|
-
typer.echo(" http://127.0.0.1:8808/mcp")
|
|
648
|
-
typer.echo("ChatGPT requires a reachable HTTPS URL. Use Secure MCP Tunnel, ngrok,")
|
|
649
|
-
typer.echo("or Cloudflare Tunnel, then create an app/connector in ChatGPT Developer mode")
|
|
650
|
-
typer.echo("with the public URL ending in /mcp, for example:")
|
|
651
|
-
typer.echo(" https://your-tunnel.example/mcp")
|
|
724
|
+
typer.echo("\nChatGPT Desktop Work Mode:")
|
|
725
|
+
typer.echo(" Add a local MCP server in the MCP configuration screen.")
|
|
726
|
+
typer.echo(" Command to launch:")
|
|
727
|
+
typer.echo(f" {binary}")
|
|
728
|
+
typer.echo(" Arguments:")
|
|
729
|
+
typer.echo(" mcp-serve")
|
|
652
730
|
typer.echo()
|
|
653
|
-
typer.echo("
|
|
654
|
-
typer.echo(
|
|
655
|
-
typer.echo(
|
|
731
|
+
typer.echo("Claude Desktop:")
|
|
732
|
+
typer.echo(" Add this to ~/Library/Application Support/Claude/claude_desktop_config.json:\n")
|
|
733
|
+
typer.echo(json.dumps(config, indent=2))
|
|
656
734
|
typer.echo()
|
|
657
735
|
|
|
658
736
|
|
|
659
737
|
@app.command()
|
|
660
738
|
def install_skill(
|
|
661
|
-
skill: str = typer.Argument("
|
|
739
|
+
skill: str = typer.Argument("agentgraph", help="Bundled skill to install"),
|
|
662
740
|
target: str = typer.Option(
|
|
663
741
|
"user",
|
|
664
742
|
"--target",
|