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