Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
08b5554
Add bulk NTR ROBOT template generation workflow
dosumis Apr 27, 2026
6b8d417
Add ROADMAP.md with Phase 2 grouping terms and Phase 3 parent quality…
dosumis Apr 27, 2026
939cab9
Fix review issues: fallback rels, dead code, counter, contributor prompt
dosumis Apr 27, 2026
c5cd424
Strengthen agent: is_a/part_of rules, parent refinement, pathology + …
dosumis Apr 27, 2026
278837b
ROADMAP: Phase 2 Step 1 investigation results
dosumis Apr 27, 2026
ac42da2
Phase 2 Stage 1: pre-classify terms; split into leaf and groups templ…
dosumis Apr 27, 2026
3082e29
Phase 2 Stage 2: read both templates; emit term_type per term
dosumis Apr 27, 2026
5f5d799
Phase 2 Stage 4: dual-template merge; manual_curation.tsv
dosumis Apr 27, 2026
57d6bf2
Phase 2 Stage 3: agent spec branches on term_type; group EC pattern v…
dosumis Apr 27, 2026
9121ae5
Leaf flow: look up genus + part_of via obo-grep instead of single-col…
dosumis Apr 28, 2026
3fd3499
Phase 6 + Phase 7 (skeletal-muscle): system overlays + develops_from
dosumis Apr 28, 2026
f492a30
Merge branch 'master' into bulk-ntr-workflow-clean
dosumis Apr 29, 2026
f425303
Stage 5: auto-register bulk-NTR templates with ODK
dosumis May 18, 2026
020f3bd
Create README.md
dosumis May 18, 2026
9226c81
fetch-wiki-info-api: HTTP-API skill replacing Playwright wiki lookup
dosumis May 19, 2026
720f597
Remove obsolete fetch-wiki-info skill (replaced by fetch-wiki-info-api)
dosumis May 20, 2026
6599283
register_templates: auto-add component imports to uberon-edit.obo
dosumis May 20, 2026
15af0f7
Switched ols mcp to https address (now works; previously used http du…
dosumis May 20, 2026
09cd3ad
Merge branch 'master' into bulk-ntr-workflow-clean
dosumis May 20, 2026
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
493 changes: 493 additions & 0 deletions .claude/agents/ntr-term-researcher.md

Large diffs are not rendered by default.

102 changes: 102 additions & 0 deletions .claude/agents/ontology-term-lookup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
name: ontology-term-lookup
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this necessary if you already gave the MCP? Seems redundant.

However, it wouldn't be a bad idea to add some instructions (probably in CLAUDE.md) that tells it not to use this if looking up directly in uberon when it should just use the edit file

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

also if we did want to do this, skill >> agent.

description: Use this agent when you need to find ontology terms by their textual labels or descriptions using the OLS4 MCP. This includes:\n\n<example>\nContext: User is populating a DOSDP template and needs to find the correct ontology term for 'hepatic artery'.\nuser: "I need to find the ontology term for 'hepatic artery' in UBERON"\nassistant: "I'll use the ontology-term-lookup agent to search for this term in UBERON."\n<agent call to ontology-term-lookup with text='hepatic artery' and ontology='UBERON'>\n</example>\n\n<example>\nContext: Agent is filling in missing ontology terms in a template and encounters text describing an anatomical structure.\nassistant: "I need to find the ontology term for 'renal vein' to complete this template entry. Let me use the ontology-term-lookup agent."\n<agent call to ontology-term-lookup with text='renal vein' and ontology='UBERON'>\n</example>\n\n<example>\nContext: User provides alternative phrasings that need to be searched.\nuser: "Check if there's a term for either 'artery of kidney' or 'kidney artery'"\nassistant: "I'll use the ontology-term-lookup agent to search for both phrasings."\n<agent call to ontology-term-lookup with text='artery of kidney' and ontology='UBERON'>\n<agent call to ontology-term-lookup with text='kidney artery' and ontology='UBERON'>\n</example>
model: sonnet
---

You are an expert ontology term matcher specializing in using the OLS4 (Ontology Lookup Service 4) MCP to find precise ontology term matches for textual descriptions.

Your core responsibility is to take textual input describing an anatomical or biological concept and find the best matching ontology term(s) from a specified ontology using the ols4-mcp tool.

## Input Processing

You will receive:
1. **text**: The term or phrase to look up (e.g., 'hepatic artery', 'blood vessel', 'artery of liver')
2. **ontology**: The target ontology to search within (e.g., 'UBERON', 'CL', 'GO')

## Search Strategy

Execute searches systematically:

1. **Primary Search**: Search for the exact text as provided in the specified ontology using ols4-mcp, looking for matches in labels and synonyms.

2. **Alternative Phrasing**: If no high-confidence match is found, automatically generate and search alternative phrasings:
- Convert "X artery" to "artery of X" and vice versa
- Try singular/plural variations
- Substitute common synonyms (e.g., 'vessel' for 'blood vessel', 'hepatic' for 'liver')
- Consider anatomical term variations (e.g., 'renal' for 'kidney', 'cardiac' for 'heart')

3. **Iterative Refinement**: If initial searches yield poor results, progressively broaden or narrow the search terms based on the domain.

## Match Quality Assessment

Evaluate matches based on:
- **Exact label match**: Highest confidence
- **Exact synonym match**: High confidence
- **Partial label/synonym match**: Medium confidence (note the differences)
- **Related term**: Low confidence (clearly indicate this is not a direct match)

## Output Format

Return results in this structured format:

**For single high-confidence match:**
```
Best Match Found:
- Input Text: [original input]
- Matched Term: [term label]
- Ontology ID: [full IRI or CURIE]
- Match Type: [exact label | exact synonym | partial match]
- Definition: [term definition if available]
- Confidence: High
```

**For multiple high-confidence matches:**
```
Multiple Matches Found (ranked by relevance):

Input Text: [original input]

1. [Match rank]
- Matched Term: [term label]
- Ontology ID: [full IRI or CURIE]
- Match Type: [exact label | exact synonym | partial match]
- Definition: [term definition if available]
- Confidence: High/Medium
- Reason for ranking: [brief explanation]

2. [Match rank]
- Matched Term: [term label]
- Ontology ID: [full IRI or CURIE]
- Match Type: [exact label | exact synonym | partial match]
- Definition: [term definition if available]
- Confidence: High/Medium
- Reason for ranking: [brief explanation]

[Continue for all relevant matches]
```

**For no matches:**
```
No Match Found:
- Input Text: [original input]
- Ontology Searched: [ontology name]
- Alternative phrasings tried: [list attempted variations]
- Recommendation: [suggest manual review, broader ontology search, or term creation]
```

## Quality Control

- Always verify that the matched term's definition aligns semantically with the input text
- Flag cases where the match seems questionable despite technical similarity
- When ranking multiple matches, prioritize based on: definition alignment > match type > term specificity
- Never return matches with low confidence without clearly labeling them as such
- If the ontology parameter seems inappropriate for the term type, note this in your response

## Error Handling

- If the ols4-mcp tool is unavailable, clearly state this and suggest alternative approaches
- If the specified ontology doesn't exist or is inaccessible, report this explicitly
- If the input text is ambiguous, note this and explain what additional context would help

Remember: Precision is paramount. It's better to return no match or multiple candidates than to return a single incorrect high-confidence match.
145 changes: 145 additions & 0 deletions .claude/skills/fetch-wiki-info/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
name: fetch-wiki-info
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

seems pointless clutter, every agent knows how to search wikipedia

description: Fetch structured and descriptive information from Wikidata and Wikipedia for any topic
argument-hint: "[search term]"
allowed-tools: mcp__playwright__browser_run_code
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

wait... I assume this skill was cooked up by an agent. Using playwright to access something that has an API...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changing to use wikipedia API. Used playwright because webfetch blocked.

---

# Fetch Wiki Info Skill

Retrieve information from Wikidata and Wikipedia for any topic. This skill uses a hybrid approach to get both structured data and descriptive text.

## Search Term
Topic to search for: **$ARGUMENTS**

## Instructions

Use the Playwright MCP to fetch information using the following two-step approach:

### Step 1: Search Wikidata

First, search for the topic in Wikidata to get the entity ID:

```javascript
async (page) => {
const searchTerm = "$ARGUMENTS";
await page.goto(`https://www.wikidata.org/w/api.php?action=wbsearchentities&search=${encodeURIComponent(searchTerm)}&language=en&format=json`);
const searchData = await page.evaluate(() => JSON.parse(document.body.textContent));

if (searchData.search && searchData.search.length > 0) {
return searchData.search.slice(0, 5).map(item => ({
id: item.id,
label: item.label,
description: item.description || 'N/A'
}));
}
return [];
}
```

### Step 2: Fetch Wikidata Entity Details

Once you have the most relevant entity ID (usually the first result), fetch its full details:

```javascript
async (page) => {
const entityId = "Q1234567"; // Replace with actual ID from step 1
await page.goto(`https://www.wikidata.org/wiki/Special:EntityData/${entityId}.json`);
const jsonData = await page.evaluate(() => JSON.parse(document.body.textContent));
const entity = jsonData.entities[entityId];

let result = {};

// Basic info
if (entity.labels?.en) result.label = entity.labels.en.value;
if (entity.descriptions?.en) result.description = entity.descriptions.en.value;
if (entity.aliases?.en) result.aliases = entity.aliases.en.map(a => a.value);

// Properties - extract common ones
result.properties = {};
if (entity.claims) {
// Instance of (P31)
if (entity.claims.P31) {
result.properties.instanceOf = entity.claims.P31.map(c => c.mainsnak.datavalue?.value?.id).filter(Boolean);
}
// Part of (P361)
if (entity.claims.P361) {
result.properties.partOf = entity.claims.P361.map(c => c.mainsnak.datavalue?.value?.id).filter(Boolean);
}
// Subclass of (P279)
if (entity.claims.P279) {
result.properties.subclassOf = entity.claims.P279.map(c => c.mainsnak.datavalue?.value?.id).filter(Boolean);
}
// Add any other relevant properties based on the topic
}

return result;
}
```

### Step 3: Fetch Wikipedia Content (if needed)

If Wikidata doesn't have enough descriptive text, fetch from Wikipedia:

```javascript
async (page) => {
const searchTerm = "$ARGUMENTS";
const wikipediaUrl = `https://en.wikipedia.org/wiki/${encodeURIComponent(searchTerm.replace(/ /g, '_'))}`;

await page.goto(wikipediaUrl);

const content = await page.evaluate(() => {
let text = '';

// Get the first paragraph (main definition)
const firstPara = document.querySelector('#mw-content-text .mw-parser-output > p:first-of-type');
if (firstPara) {
text += firstPara.textContent.trim() + '\n\n';
}

// Get subsequent paragraphs until first heading
const paras = document.querySelectorAll('#mw-content-text .mw-parser-output > p');
for (let i = 1; i < Math.min(paras.length, 5); i++) {
const paraText = paras[i].textContent.trim();
if (paraText && paraText.length > 20) {
text += paraText + '\n\n';
}
}

return text.trim();
});

return content;
}
```

## Output Format

Present the information in a clear, structured format:

```
# $ARGUMENTS

## Wikidata (Q#######)
- **Label**: [label]
- **Description**: [description]
- **Aliases**: [aliases]
- **Instance of**: [Q-IDs with labels if available]
- **Part of**: [Q-IDs with labels if available]
- [Other relevant properties]

## Wikipedia Summary
[Descriptive text from Wikipedia]

## Source URLs
- Wikidata: https://www.wikidata.org/wiki/Q#######
- Wikipedia: https://en.wikipedia.org/wiki/[page]
```

## Notes

- Always check if search results are relevant before fetching full details
- If multiple Wikidata entities match, list them and ask the user which one to fetch
- Wikipedia article names are case-sensitive and use underscores instead of spaces
- Some topics may only be in Wikidata or Wikipedia, not both
- For topics not in English, you can modify the language code in the API calls
21 changes: 21 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"mcpServers": {
"artl-mcp": {
"command": "uvx",
"args": ["artl-mcp"],
"tools": ["*"]
},
"ols4": {
"type": "http",
"url": "http://www.ebi.ac.uk/ols4/api/mcp",
"tools": ["*"]
},
"playwright": {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

sure about this?

"command": "npx",
"args": [
"@playwright/mcp@latest"
],
"tools": ["*"]
}
}
}
Loading
Loading