Skip to content

Commit 7773f79

Browse files
authored
Merge pull request #1 from opencontextprotocol/feat/parser-registry-pattern
feat!: refactor tool discovery into extensible parser registry pattern
2 parents 3722286 + 83bc236 commit 7773f79

10 files changed

Lines changed: 1378 additions & 1142 deletions

src/ocp_agent/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from .agent import OCPAgent
1818
from .storage import OCPStorage
1919
from .errors import OCPError, RegistryUnavailable, APINotFound, SchemaDiscoveryError, ValidationError
20+
from .parsers import APISpecParser, ParserRegistry, OpenAPIParser
2021

2122
__version__ = "0.1.0"
2223
__all__ = [
@@ -30,7 +31,7 @@
3031
"extract_context_from_response",
3132
"validate_context",
3233

33-
# Convenience functions for cleaner API
34+
# Convenience functions
3435
"parse_context",
3536
"add_context_headers",
3637

@@ -39,6 +40,11 @@
3940
"OCPTool",
4041
"OCPAPISpec",
4142

43+
# Parser system
44+
"APISpecParser",
45+
"ParserRegistry",
46+
"OpenAPIParser",
47+
4248
# Registry integration
4349
"OCPRegistry",
4450

src/ocp_agent/parsers/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
API Specification Parsers
3+
4+
This module provides an extensible parser system for converting various API
5+
specification formats (OpenAPI, Postman, GraphQL, etc.) into OCP tools.
6+
"""
7+
8+
from .base import APISpecParser
9+
from .registry import ParserRegistry
10+
from .openapi_parser import OpenAPIParser
11+
12+
__all__ = [
13+
'APISpecParser',
14+
'ParserRegistry',
15+
'OpenAPIParser',
16+
]

src/ocp_agent/parsers/base.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""
2+
Base Parser Interface
3+
4+
Defines the abstract interface that all API specification parsers must implement.
5+
"""
6+
7+
from abc import ABC, abstractmethod
8+
from typing import Dict, Any, Optional, List
9+
from dataclasses import dataclass
10+
11+
12+
@dataclass
13+
class OCPTool:
14+
"""Represents a discovered API tool/endpoint"""
15+
name: str
16+
description: str
17+
method: str
18+
path: str
19+
parameters: Dict[str, Any]
20+
response_schema: Optional[Dict[str, Any]]
21+
operation_id: Optional[str] = None
22+
tags: Optional[List[str]] = None
23+
24+
25+
@dataclass
26+
class OCPAPISpec:
27+
"""Represents a parsed API specification"""
28+
base_url: str
29+
title: str
30+
version: str
31+
description: str
32+
tools: List[OCPTool]
33+
raw_spec: Dict[str, Any]
34+
name: Optional[str] = None
35+
36+
37+
class APISpecParser(ABC):
38+
"""
39+
Abstract base class for API specification parsers.
40+
41+
All parsers must implement three methods:
42+
- can_parse(): Detect if this parser can handle a given spec
43+
- parse(): Convert the spec into an OCPAPISpec with tools
44+
- get_format_name(): Return a human-readable format name
45+
"""
46+
47+
@abstractmethod
48+
def can_parse(self, spec_data: Dict[str, Any]) -> bool:
49+
"""
50+
Determine if this parser can handle the given specification.
51+
52+
Args:
53+
spec_data: The raw specification data as a dictionary
54+
55+
Returns:
56+
True if this parser can handle the format, False otherwise
57+
"""
58+
pass
59+
60+
@abstractmethod
61+
def parse(
62+
self,
63+
spec_data: Dict[str, Any],
64+
base_url_override: Optional[str] = None,
65+
include_resources: Optional[List[str]] = None,
66+
path_prefix: Optional[str] = None
67+
) -> OCPAPISpec:
68+
"""
69+
Parse the specification and extract tools.
70+
71+
Args:
72+
spec_data: The raw specification data as a dictionary
73+
base_url_override: Optional override for the API base URL
74+
include_resources: Optional list of resource names to filter tools by
75+
path_prefix: Optional path prefix to strip before filtering
76+
77+
Returns:
78+
OCPAPISpec containing extracted tools and metadata
79+
80+
Raises:
81+
Exception: If parsing fails
82+
"""
83+
pass
84+
85+
@abstractmethod
86+
def get_format_name(self) -> str:
87+
"""
88+
Get the human-readable name of the format this parser handles.
89+
90+
Returns:
91+
Format name (e.g., "OpenAPI", "Postman Collection", "GraphQL")
92+
"""
93+
pass

0 commit comments

Comments
 (0)