Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 222 additions & 0 deletions skills/knowledge-graph-mcp/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
---
name: knowledge-graph-mcp
description: Build an MCP (Model Context Protocol) server that exposes a Markdown-LD knowledge graph to AI agents. Provides tool definitions for SPARQL queries, natural language questions, entity listing, and article discovery. Use when creating an MCP server to make a knowledge bank queryable by LLMs and agent frameworks.
---

# Knowledge Graph MCP Server

Build an [MCP server](https://modelcontextprotocol.io/) that wraps a Markdown-LD knowledge bank, letting any AI agent query the knowledge graph through structured tool calls.

## Why MCP?

The knowledge bank already has HTTP endpoints (`/api/sparql`, `/api/ask`). An MCP server adds:

- **Agent discoverability** — Agents automatically discover available tools
- **Structured input/output** — Zod/Pydantic schemas for parameters and results
- **Tool annotations** — Read-only hints, descriptions that help agents choose tools
- **Local or remote** — Works via stdio (local) or streamable HTTP (remote)

## Architecture

```
Agent (Claude, Copilot, etc.)
↕ MCP Protocol (stdio or HTTP)
MCP Server
↕ RDFLib (local) or HTTP (remote)
Knowledge Graph (Turtle files)
```

Two deployment modes:

1. **Local mode** — MCP server loads `.ttl` files directly into RDFLib
2. **Remote mode** — MCP server proxies to the deployed Azure Functions API

Comment on lines +29 to +33
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section describes a “Remote mode” that proxies to a deployed API, but the reference implementation in scripts/server.py does not currently implement an --api-url option or any HTTP proxy behavior. Please either implement the remote mode or remove/clarify these deployment-mode claims so the skill stays accurate.

Copilot uses AI. Check for mistakes.
## Tool Definitions

### 1. `query_sparql`

Execute a SPARQL query against the knowledge graph.

```python
@mcp.tool(
annotations={
"readOnlyHint": True,
"openWorldHint": False,
}
)
def query_sparql(query: str) -> str:
"""Execute a SPARQL 1.1 SELECT or ASK query against the knowledge graph.

The graph uses schema.org vocabulary. Available prefixes:
- schema: <https://schema.org/>
- kb: <https://example.com/vocab/kb#>
- prov: <http://www.w3.org/ns/prov#>

Only SELECT and ASK queries are allowed. Include a LIMIT clause.

Args:
query: A valid SPARQL 1.1 query string with PREFIX declarations.

Returns:
JSON string with SPARQL Results JSON format.
"""
```

### 2. `ask_question`

Natural language query — the server translates to SPARQL.

```python
@mcp.tool(
annotations={
"readOnlyHint": True,
"openWorldHint": False,
}
)
def ask_question(question: str) -> str:
"""Ask a natural language question about the knowledge graph.

The question is translated to SPARQL and executed. The response
includes both the generated SPARQL and the results.

Example questions:
- "What entities are in the knowledge graph?"
- "Which articles mention SPARQL?"
- "Find all organizations"

Args:
question: A natural language question about the knowledge.

Returns:
JSON with 'question', 'sparql', and 'results' fields.
"""
```
Comment on lines +65 to +93
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The skill defines an ask_question MCP tool, but scripts/server.py does not implement an ask_question function/tool. This is likely to confuse consumers following the skill; either add the tool (proxying to /api/ask or performing local NL→SPARQL) or remove this tool definition from the doc.

Copilot uses AI. Check for mistakes.

### 3. `list_entities`

Browse entities with optional type filter.

```python
@mcp.tool(
annotations={
"readOnlyHint": True,
"openWorldHint": False,
}
)
def list_entities(entity_type: str = "schema:Thing", limit: int = 50) -> str:
"""List entities in the knowledge graph, optionally filtered by type.

Available types: schema:Person, schema:Organization,
schema:SoftwareApplication, schema:CreativeWork, schema:Thing

Args:
entity_type: Schema.org type to filter by (default: all entities).
limit: Maximum number of results (default: 50).

Returns:
JSON array of entities with id, name, and type.
"""
```

### 4. `list_articles`

Discover articles in the knowledge bank.

```python
@mcp.tool(
annotations={
"readOnlyHint": True,
"openWorldHint": False,
}
)
def list_articles(limit: int = 50) -> str:
"""List articles in the knowledge bank.

Args:
limit: Maximum number of results (default: 50).

Returns:
JSON array of articles with id, title, datePublished, and keywords.
"""
```

### 5. `get_entity_details`

Deep-dive into a specific entity's relationships.

```python
@mcp.tool(
annotations={
"readOnlyHint": True,
"openWorldHint": False,
}
)
def get_entity_details(entity_name: str) -> str:
"""Get detailed information about a specific entity.

Returns the entity's type, sameAs links, articles that mention it,
and related entities.

Args:
entity_name: The name of the entity to look up (case-insensitive).

Returns:
JSON with entity details and relationships.
"""
```

## Implementation (Python / FastMCP)

See [scripts/server.py](scripts/server.py) for a complete reference implementation.

### Quick Start

```bash
# Install dependencies
pip install mcp rdflib

# Run locally (stdio transport)
python scripts/server.py --graph-dir ./graph/articles

# Run as HTTP server
python scripts/server.py --transport http --port 8080
```

### Connecting to an Agent

**Claude Code / Copilot:**
```json
{
"mcpServers": {
"knowledge-graph": {
"command": "python",
"args": ["skills/knowledge-graph-mcp/scripts/server.py", "--graph-dir", "./graph/articles"]
}
}
}
```

## Safety

All tools are read-only:
- SPARQL queries are validated: `INSERT`, `DELETE`, `LOAD`, `CLEAR`, `DROP`, `CREATE` are blocked
- A `LIMIT` clause is injected if missing (default 100)
- No authentication required for read access

## Error Handling

Return clear, actionable errors:

```python
if not is_valid:
return json.dumps({
"error": f"Invalid SPARQL syntax: {error_msg}",
"hint": "Check PREFIX declarations and property names. "
"Available properties: schema:name, schema:mentions, schema:about, ..."
})
```

## Reference

- [references/api-reference.md](references/api-reference.md) — Full API endpoint documentation
- [scripts/server.py](scripts/server.py) — Reference MCP server implementation
131 changes: 131 additions & 0 deletions skills/knowledge-graph-mcp/references/api-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# API Reference

The Markdown-LD knowledge bank exposes two HTTP endpoints via Azure Functions.

## `GET/POST /api/sparql`

Execute SPARQL 1.1 queries against the knowledge graph.

### GET

```
GET /api/sparql?query={url-encoded-sparql}
```

### POST (preferred for complex queries)

```
POST /api/sparql
Content-Type: application/sparql-query

PREFIX schema: <https://schema.org/>
SELECT ?name WHERE { ?e schema:name ?name }
```

### POST (form-encoded)

```
POST /api/sparql
Content-Type: application/x-www-form-urlencoded

query=PREFIX+schema...
```

### Response

```
Content-Type: application/sparql-results+json

{
"head": { "vars": ["name"] },
"results": {
"bindings": [
{ "name": { "type": "literal", "value": "Neo4j" } }
]
}
}
```

### Errors

- `400` — Missing query parameter or SPARQL syntax error
- `403` — Mutating query detected (INSERT, DELETE, etc.)

### Headers

```
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=300
```

## `GET/POST /api/ask`

Translate natural language to SPARQL and execute.

### GET

```
GET /api/ask?question={url-encoded-question}
```

### POST (preferred)

```
POST /api/ask
Content-Type: application/json

{"question": "What entities are in the knowledge graph?"}
```

### Response

```json
{
"question": "What entities are in the knowledge graph?",
"sparql": "PREFIX schema: <https://schema.org/>\nSELECT DISTINCT ?entity ?name ?type WHERE {\n ?entity a ?type ;\n schema:name ?name .\n FILTER(?type != schema:Article)\n}\nLIMIT 100",
"results": {
"head": { "vars": ["entity", "name", "type"] },
"results": { "bindings": [...] }
}
}
```

### Errors

- `400` — Missing question parameter or SPARQL execution error
- `502` — LLM rate limit or API error

### Requirements

The `/api/ask` endpoint requires `GITHUB_TOKEN` as an environment variable for LLM access (GitHub Models). The `/api/sparql` endpoint works without any configuration.

## Available Schema

### Classes

| Class | Description |
|-------|-------------|
| `schema:Article` | A Markdown article |
| `schema:Person` | A person |
| `schema:Organization` | A company, foundation, or team |
| `schema:SoftwareApplication` | Software tools and libraries |
| `schema:CreativeWork` | Books, papers, specs |
| `schema:Thing` | Base type for any entity |

### Properties

| Property | Domain → Range |
|----------|---------------|
| `schema:name` | Any → string |
| `schema:mentions` | Article → Thing |
| `schema:about` | Article → Thing |
| `schema:author` | Article → Person |
| `schema:creator` | Thing → Person/Org |
| `schema:datePublished` | Article → date |
| `schema:dateModified` | Article → date |
| `schema:sameAs` | Thing → URI |
| `schema:keywords` | Article → string |
| `schema:description` | Article → string |
| `kb:relatedTo` | Thing → Thing |
| `kb:confidence` | Assertion → decimal |
| `prov:wasDerivedFrom` | Assertion → URI |
Loading