Skip to main content

Sagittarius Knowledge Graph

Sagittarius is the built-in knowledge graph that automatically builds a persistent model of your organization as you use the API. Every API operation enriches the graph with entities and relationships.

Why a Knowledge Graph?​

Traditional APIs are transactional - you ask for data, you get data, it's forgotten. But understanding your organization requires context:

  • Who authored this PR?
  • What issues are assigned to Alice?
  • Which repos had deployments this week?
  • How do these projects relate to each other?

Sagittarius answers these questions by maintaining a persistent graph of entities and their relationships.

How It Works​

┌─────────────────────────────────────────────────────────────────┐
│ API Request │
│ POST /mcp/jira/execute {"tool": "list_issues"} │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────┐
│ Jira Adapter │
│ Fetches data │
└────────┬────────┘
│
┌──────────────┴──────────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────────┐
│ Return response │ │ Sagittarius Bridge │
│ to caller │ │ │
└─────────────────┘ │ For each issue: │
│ → Create Issue node │
│ → Create User nodes │
│ → Create edges │
│ → Log activity │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Redis (DB 1) │
│ Persistent storage │
└─────────────────────┘

Key insight: The graph builds passively as you use the API. No separate sync jobs, no ETL pipelines.

Entity Types​

Node TypeSourceKey Properties
userGitHub, Jiralogin, name, email, account_id
repositoryGitHubfull_name, language, description
pull_requestGitHubnumber, title, state, author
issueJirakey, summary, status, priority
projectJirakey, name, lead
pageConfluenceid, title, space_key
deploymentGitHubid, environment, status
notion_pageNotionid, title, database_id
notion_databaseNotionid, title

Relationship Types​

Edge TypeFromToMeaning
AUTHORED_BYPR, Issue, PageUserWho created it
ASSIGNED_TOIssueUserWho's working on it
BELONGS_TOPR, Issue, PageRepo, Project, SpaceContainment
DEPLOYED_TODeploymentRepositoryWhere deployed
CREATED_BYProjectUserProject lead
CONTRIBUTES_TOUserRepo, ProjectContribution
LINKED_TOIssue, PageIssue, PRCross-references

API Endpoints​

Get Entity​

POST /graph/entity
{
"entity_type": "user",
"entity_id": "alice"
}

Returns the node with all properties.

Traverse Graph​

POST /graph/traverse
{
"entity_type": "user",
"entity_id": "alice",
"depth": 2,
"direction": "both"
}

Returns all nodes and edges within N hops of the starting node.

Response:

{
"success": true,
"data": {
"nodes": [
{"node_type": "user", "node_id": "alice", "properties": {...}},
{"node_type": "pull_request", "node_id": "org/repo/123", "properties": {...}},
{"node_type": "repository", "node_id": "org/repo", "properties": {...}}
],
"edges": [
{"from_node": "pull_request:org/repo/123", "to_node": "user:alice", "edge_type": "AUTHORED_BY"},
{"from_node": "pull_request:org/repo/123", "to_node": "repository:org/repo", "edge_type": "BELONGS_TO"}
]
}
}

Search Entities​

POST /graph/search
{
"entity_type": "issue",
"filters": {"status": "In Progress"},
"limit": 50
}

Returns entities matching the filter criteria.

Find Path​

POST /graph/find-path
{
"from_type": "user",
"from_id": "alice",
"to_type": "repository",
"to_id": "org/api",
"max_depth": 5
}

Returns the shortest path between two entities.

Activity Feed​

GET /graph/feed?limit=50

Returns recent activity across all entities.

Entity Activity​

POST /graph/activity
{
"entity_type": "user",
"entity_id": "alice",
"limit": 20
}

Returns activity timeline for a specific entity.

Graph Stats​

GET /graph/stats

Returns counts by entity type and total edges.

Response:

{
"success": true,
"data": {
"total_nodes": 156,
"total_edges": 423,
"nodes_by_type": {
"user": 15,
"repository": 8,
"pull_request": 45,
"issue": 67,
"project": 5,
"deployment": 16
}
}
}

Graph Visualizer​

Interactive web UI for exploring the knowledge graph.

URL: http://localhost:3007/graph/visualizer

Features​

  • Entity Type Selection: Pick one type or "All Types"
  • Entity ID Modes: All, Single, or Multi-select
  • Depth Control: 1-3 hops or all connections
  • Quick Actions: Toggle entity types to combine
  • Cypher-like Queries: Write graph queries
  • Activity Feed: See recent operations
  • Stats Dashboard: Node and edge counts

Query Syntax​

// Find user with relationships
MATCH (u:user) WHERE u.id = 'alice'

// Find all entities of type
MATCH (n:repository)

// Relationship query
MATCH (u:user)-[r]-(e) WHERE u.id = 'bob'

// Path finding
PATH FROM user:alice TO repository:acme/api

Activity Logging​

Every MCP execution is automatically logged:

# Automatic logging in MCP layer
await bridge.record_activity(
action="mcp.create_issue",
actor_type="user",
actor_id="alice@company.com", # From JWT
target_type="issue",
target_id="PROJ-123",
context_type="tenant",
context_id="tenant-abc",
properties={
"system": "jira",
"tool": "create_issue",
"success": True
}
)

This creates a complete audit trail of who did what, when.

Persistence​

Sagittarius uses Redis with full persistence:

# docker-compose.yml
redis:
command: redis-server --appendonly yes --save 60 1
volumes:
- ./data/redis:/data

Storage location: ./data/redis/

  • appendonly.aof - Transaction log
  • dump.rdb - Periodic snapshots

Data survives container restarts and host reboots.

Bridge Methods​

The Sagittarius Bridge provides typed methods for each entity:

from app.integrations.sagittarius_bridge import get_sagittarius_bridge

bridge = await get_sagittarius_bridge()

# Ingest entities
await bridge.on_user_fetched(user_data, source="github")
await bridge.on_repository_fetched(repo_data)
await bridge.on_pull_request_fetched(pr_data, repo_id)
await bridge.on_issue_fetched(issue_data)
await bridge.on_project_fetched(project_data)
await bridge.on_page_fetched(page_data)
await bridge.on_deployment_fetched(deploy_data, repo_id)

# Record activity
await bridge.record_activity(
action="reviewed",
actor_type="user",
actor_id="alice",
target_type="pull_request",
target_id="org/repo/123"
)

Auto-Entity Creation​

When ingesting entities with relationships, Sagittarius automatically creates related entities to ensure relationship targets exist:

# When ingesting an issue:
# 1. Creates reporter user (if data available)
# 2. Creates assignee user (if data available)
# 3. Creates issue node
# 4. Creates edges: AUTHORED_BY, ASSIGNED_TO, BELONGS_TO

This ensures the graph is always consistent, even if you haven't explicitly fetched all related entities.

Use Cases​

1. Context for AI Agents​

"What PRs has Alice authored in the last week?"
→ Query: MATCH (u:user)-[:AUTHORED_BY]-(pr:pull_request) WHERE u.id = 'alice'

2. Impact Analysis​

"What's connected to this repository?"
→ Traverse from repository with depth=2

3. Audit Trail​

"Who modified this project's issues?"
→ GET /graph/activity for project entity

4. Cross-System Correlation​

"Show all entities related to user alice across all systems"
→ Traverse from user node, includes GitHub PRs, Jira issues, Confluence pages

5. Onboarding Context​

"What does the new hire need to know about?"
→ Traverse from their user node to see assigned issues, relevant repos

Architecture​

/sagittarius # Standalone package
├── core/
│ ├── models.py # Node, Edge, NodeRef, Activity
│ ├── schema.py # NodeType, EdgeType enums
│ └── exceptions.py # Custom errors
├── storage/
│ ├── base.py # Storage interface
│ ├── redis_storage.py # Redis implementation
│ └── memory_storage.py # In-memory (for tests)
├── ingest/
│ ├── event_types.py # EntityCreated, ActivityRecorded
│ └── handlers.py # Event processing
├── service/
│ └── graph_service.py # Query operations, SagittariusClient
└── config.py # Configuration

Sagittarius is designed to be spun off as its own service - it has zero dependencies on the main app.

Configuration​

Environment variables:

VariableDefaultDescription
SAGITTARIUS_REDIS_URLredis://localhost:6380/1Redis connection
SAGITTARIUS_KEY_PREFIXsagKey prefix for all Redis keys
SAGITTARIUS_FRESHNESS_TTL300Freshness check TTL (seconds)
SAGITTARIUS_SYNC_INTERVAL900Background sync interval