@opencontextprotocol/agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/src/agent.d.ts +112 -0
- package/dist/src/agent.d.ts.map +1 -0
- package/dist/src/agent.js +358 -0
- package/dist/src/agent.js.map +1 -0
- package/dist/src/context.d.ts +108 -0
- package/dist/src/context.d.ts.map +1 -0
- package/dist/src/context.js +196 -0
- package/dist/src/context.js.map +1 -0
- package/dist/src/errors.d.ts +40 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +63 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/headers.d.ts +63 -0
- package/dist/src/headers.d.ts.map +1 -0
- package/dist/src/headers.js +238 -0
- package/dist/src/headers.js.map +1 -0
- package/dist/src/http_client.d.ts +82 -0
- package/dist/src/http_client.d.ts.map +1 -0
- package/dist/src/http_client.js +181 -0
- package/dist/src/http_client.js.map +1 -0
- package/dist/src/index.d.ts +25 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +35 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/registry.d.ts +52 -0
- package/dist/src/registry.d.ts.map +1 -0
- package/dist/src/registry.js +164 -0
- package/dist/src/registry.js.map +1 -0
- package/dist/src/schema_discovery.d.ts +149 -0
- package/dist/src/schema_discovery.d.ts.map +1 -0
- package/dist/src/schema_discovery.js +707 -0
- package/dist/src/schema_discovery.js.map +1 -0
- package/dist/src/schemas/ocp-context.json +138 -0
- package/dist/src/storage.d.ts +110 -0
- package/dist/src/storage.d.ts.map +1 -0
- package/dist/src/storage.js +399 -0
- package/dist/src/storage.js.map +1 -0
- package/dist/src/validation.d.ts +169 -0
- package/dist/src/validation.d.ts.map +1 -0
- package/dist/src/validation.js +92 -0
- package/dist/src/validation.js.map +1 -0
- package/dist/tests/agent.test.d.ts +5 -0
- package/dist/tests/agent.test.d.ts.map +1 -0
- package/dist/tests/agent.test.js +536 -0
- package/dist/tests/agent.test.js.map +1 -0
- package/dist/tests/context.test.d.ts +5 -0
- package/dist/tests/context.test.d.ts.map +1 -0
- package/dist/tests/context.test.js +285 -0
- package/dist/tests/context.test.js.map +1 -0
- package/dist/tests/headers.test.d.ts +5 -0
- package/dist/tests/headers.test.d.ts.map +1 -0
- package/dist/tests/headers.test.js +356 -0
- package/dist/tests/headers.test.js.map +1 -0
- package/dist/tests/http_client.test.d.ts +5 -0
- package/dist/tests/http_client.test.d.ts.map +1 -0
- package/dist/tests/http_client.test.js +373 -0
- package/dist/tests/http_client.test.js.map +1 -0
- package/dist/tests/registry.test.d.ts +5 -0
- package/dist/tests/registry.test.d.ts.map +1 -0
- package/dist/tests/registry.test.js +232 -0
- package/dist/tests/registry.test.js.map +1 -0
- package/dist/tests/schema_discovery.test.d.ts +5 -0
- package/dist/tests/schema_discovery.test.d.ts.map +1 -0
- package/dist/tests/schema_discovery.test.js +1074 -0
- package/dist/tests/schema_discovery.test.js.map +1 -0
- package/dist/tests/storage.test.d.ts +5 -0
- package/dist/tests/storage.test.d.ts.map +1 -0
- package/dist/tests/storage.test.js +414 -0
- package/dist/tests/storage.test.js.map +1 -0
- package/dist/tests/validation.test.d.ts +5 -0
- package/dist/tests/validation.test.d.ts.map +1 -0
- package/dist/tests/validation.test.js +254 -0
- package/dist/tests/validation.test.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 NALLENSCOTT, LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# OCP JavaScript Library
|
|
2
|
+
|
|
3
|
+
Context-aware HTTP client framework for AI agents.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @opencontextprotocol/agent
|
|
9
|
+
# or with yarn
|
|
10
|
+
yarn add @opencontextprotocol/agent
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { OCPAgent } from '@opencontextprotocol/agent';
|
|
17
|
+
|
|
18
|
+
// Create an OCP agent
|
|
19
|
+
const agent = new OCPAgent(
|
|
20
|
+
'api_explorer',
|
|
21
|
+
'your-username',
|
|
22
|
+
'my-project',
|
|
23
|
+
'Explore GitHub API'
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// Register an API from the registry (fast lookup)
|
|
27
|
+
const githubApi = await agent.registerApi('github');
|
|
28
|
+
|
|
29
|
+
// Or register from OpenAPI specification URL
|
|
30
|
+
// const githubApi = await agent.registerApi(
|
|
31
|
+
// 'github',
|
|
32
|
+
// 'https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/api.github.com.json'
|
|
33
|
+
// );
|
|
34
|
+
|
|
35
|
+
// List available tools
|
|
36
|
+
const tools = agent.listTools('github');
|
|
37
|
+
console.log(`Found ${tools.length} GitHub API tools`);
|
|
38
|
+
|
|
39
|
+
// Call a tool
|
|
40
|
+
const result = await agent.callTool(
|
|
41
|
+
'usersGetAuthenticated',
|
|
42
|
+
undefined,
|
|
43
|
+
'github'
|
|
44
|
+
);
|
|
45
|
+
console.log(result);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## API Registration & Authentication
|
|
49
|
+
|
|
50
|
+
The `registerApi()` method supports multiple patterns:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// 1. Registry lookup - fastest, uses community registry
|
|
54
|
+
const githubApi = await agent.registerApi('github');
|
|
55
|
+
|
|
56
|
+
// 2. Registry lookup with authentication
|
|
57
|
+
const githubApi = await agent.registerApi(
|
|
58
|
+
'github',
|
|
59
|
+
undefined,
|
|
60
|
+
undefined,
|
|
61
|
+
{ 'Authorization': 'token ghp_your_token_here' }
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// 3. Registry lookup with base URL override (e.g., GitHub Enterprise)
|
|
65
|
+
const gheApi = await agent.registerApi(
|
|
66
|
+
'github',
|
|
67
|
+
undefined,
|
|
68
|
+
'https://github.company.com/api/v3',
|
|
69
|
+
{ 'Authorization': 'token ghp_enterprise_token' }
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// 4. Direct OpenAPI spec URL
|
|
73
|
+
const api = await agent.registerApi(
|
|
74
|
+
'my-api',
|
|
75
|
+
'https://api.example.com/openapi.json'
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// 5. Direct OpenAPI spec with base URL override and authentication
|
|
79
|
+
const api = await agent.registerApi(
|
|
80
|
+
'my-api',
|
|
81
|
+
'https://api.example.com/openapi.json',
|
|
82
|
+
'https://staging-api.example.com', // Override for testing
|
|
83
|
+
{ 'X-API-Key': 'your_api_key_here' }
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// 6. Local OpenAPI file (JSON or YAML)
|
|
87
|
+
const api = await agent.registerApi(
|
|
88
|
+
'local-api',
|
|
89
|
+
'file:///path/to/openapi.yaml',
|
|
90
|
+
'http://localhost:8000'
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Headers are automatically included in all tool calls
|
|
94
|
+
const result = await agent.callTool('usersGetAuthenticated', undefined, 'github');
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Core Components
|
|
98
|
+
|
|
99
|
+
- **OCPAgent**: Main agent class with API discovery and tool invocation
|
|
100
|
+
- **AgentContext**: Context management with persistent conversation tracking
|
|
101
|
+
- **OCPHTTPClient**: Context-aware HTTP client wrapper
|
|
102
|
+
- **OCPSchemaDiscovery**: OpenAPI specification parsing and tool extraction
|
|
103
|
+
- **Headers**: OCP context encoding/decoding for HTTP headers
|
|
104
|
+
- **Validation**: JSON schema validation for context objects
|
|
105
|
+
|
|
106
|
+
## API Reference
|
|
107
|
+
|
|
108
|
+
### OCPAgent
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const agent = new OCPAgent(agentType, user?, workspace?, agentGoal?, registryUrl?, enableCache?);
|
|
112
|
+
await agent.registerApi(name, specUrl?, baseUrl?, headers?);
|
|
113
|
+
agent.listTools(apiName?);
|
|
114
|
+
await agent.callTool(toolName, parameters?, apiName?);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### AgentContext
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const context = new AgentContext({ agent_type, user?, workspace? });
|
|
121
|
+
context.addInteraction(action, apiEndpoint?, result?, metadata?);
|
|
122
|
+
context.updateGoal(newGoal, summary?);
|
|
123
|
+
context.toDict();
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### HTTP Client
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { OCPHTTPClient, AgentContext } from '@opencontextprotocol/agent';
|
|
130
|
+
|
|
131
|
+
// Create context
|
|
132
|
+
const context = new AgentContext({
|
|
133
|
+
agent_type: 'api_client',
|
|
134
|
+
user: 'username',
|
|
135
|
+
workspace: 'project'
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Create OCP-aware HTTP client
|
|
139
|
+
const client = new OCPHTTPClient(context, true, 'https://api.example.com');
|
|
140
|
+
|
|
141
|
+
// Make requests with automatic OCP context headers
|
|
142
|
+
const response = await client.request('GET', '/endpoint');
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Clone repository
|
|
149
|
+
git clone https://github.com/opencontextprotocol/ocp-javascript.git
|
|
150
|
+
cd ocp-javascript
|
|
151
|
+
|
|
152
|
+
# Install dependencies
|
|
153
|
+
npm install
|
|
154
|
+
|
|
155
|
+
# Run tests
|
|
156
|
+
npm test
|
|
157
|
+
|
|
158
|
+
# Run tests with coverage
|
|
159
|
+
npm test -- --coverage
|
|
160
|
+
|
|
161
|
+
# Build for distribution
|
|
162
|
+
npm run build
|
|
163
|
+
|
|
164
|
+
# Run specific test file
|
|
165
|
+
npm test context.test.ts
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Project Structure
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
src/
|
|
172
|
+
├── index.ts # Public API exports
|
|
173
|
+
├── agent.ts # OCPAgent class
|
|
174
|
+
├── context.ts # AgentContext class
|
|
175
|
+
├── http_client.ts # HTTP client wrappers
|
|
176
|
+
├── headers.ts # Header encoding/decoding
|
|
177
|
+
├── schema_discovery.ts # OpenAPI parsing
|
|
178
|
+
├── registry.ts # Registry client
|
|
179
|
+
├── validation.ts # JSON schema validation
|
|
180
|
+
└── errors.ts # Error classes
|
|
181
|
+
|
|
182
|
+
tests/
|
|
183
|
+
├── agent.test.ts # OCPAgent tests
|
|
184
|
+
├── context.test.ts # AgentContext tests
|
|
185
|
+
├── http_client.test.ts # HTTP client tests
|
|
186
|
+
├── headers.test.ts # Header tests
|
|
187
|
+
├── schema_discovery.test.ts # Schema parsing tests
|
|
188
|
+
├── registry.test.ts # Registry tests
|
|
189
|
+
└── validation.test.ts # Validation tests
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## TypeScript Support
|
|
193
|
+
|
|
194
|
+
This library is written in TypeScript and includes full type definitions. All exports are fully typed for excellent IDE support and type safety.
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT License - see LICENSE file.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OCP Agent - Context-Aware API Discovery and Execution
|
|
3
|
+
*
|
|
4
|
+
* Combines OCP's context management with automatic API discovery,
|
|
5
|
+
* providing intelligent API interactions with zero infrastructure.
|
|
6
|
+
*/
|
|
7
|
+
import { AgentContext } from './context.js';
|
|
8
|
+
import { OCPSchemaDiscovery, OCPAPISpec, OCPTool } from './schema_discovery.js';
|
|
9
|
+
import { OCPHTTPClient, OCPResponse } from './http_client.js';
|
|
10
|
+
import { OCPRegistry } from './registry.js';
|
|
11
|
+
import { OCPStorage } from './storage.js';
|
|
12
|
+
/**
|
|
13
|
+
* OCP Agent for Context-Aware API Interactions
|
|
14
|
+
*
|
|
15
|
+
* Provides:
|
|
16
|
+
* 1. API Discovery (from OpenAPI specs or community registry)
|
|
17
|
+
* 2. Tool Invocation (with parameter validation)
|
|
18
|
+
* 3. Context Management (persistent across calls)
|
|
19
|
+
* 4. Zero Infrastructure (no servers required)
|
|
20
|
+
*/
|
|
21
|
+
export declare class OCPAgent {
|
|
22
|
+
context: AgentContext;
|
|
23
|
+
discovery: OCPSchemaDiscovery;
|
|
24
|
+
registry: OCPRegistry;
|
|
25
|
+
knownApis: Map<string, OCPAPISpec>;
|
|
26
|
+
apiClients: Map<string, OCPHTTPClient>;
|
|
27
|
+
httpClient: OCPHTTPClient;
|
|
28
|
+
storage: OCPStorage | null;
|
|
29
|
+
/**
|
|
30
|
+
* Initialize OCP Agent with context and schema discovery.
|
|
31
|
+
*
|
|
32
|
+
* @param agentType - Type of AI agent (e.g., "ide_coding_assistant")
|
|
33
|
+
* @param user - User identifier
|
|
34
|
+
* @param workspace - Current workspace/project
|
|
35
|
+
* @param agentGoal - Current objective or goal
|
|
36
|
+
* @param registryUrl - Custom registry URL (uses OCP_REGISTRY_URL env var or default if not provided)
|
|
37
|
+
* @param enableCache - Enable local API caching and session persistence (default: true)
|
|
38
|
+
*/
|
|
39
|
+
constructor(agentType?: string, user?: string, workspace?: string, agentGoal?: string, registryUrl?: string, enableCache?: boolean);
|
|
40
|
+
/**
|
|
41
|
+
* Register an API for discovery and usage.
|
|
42
|
+
*
|
|
43
|
+
* @param name - Human-readable name for the API or registry API name
|
|
44
|
+
* @param specUrl - URL to OpenAPI specification (optional if using registry lookup)
|
|
45
|
+
* @param baseUrl - Optional override for API base URL
|
|
46
|
+
* @param headers - Optional headers for authenticated requests to this API
|
|
47
|
+
* @returns Discovered API specification with available tools
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* // Registry lookup (fast)
|
|
51
|
+
* await agent.registerApi('github');
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // Direct OpenAPI discovery
|
|
55
|
+
* await agent.registerApi('my-api', 'https://api.example.com/openapi.json');
|
|
56
|
+
*/
|
|
57
|
+
registerApi(name: string, specUrl?: string, baseUrl?: string, headers?: Record<string, string>): Promise<OCPAPISpec>;
|
|
58
|
+
/**
|
|
59
|
+
* List available tools.
|
|
60
|
+
*
|
|
61
|
+
* @param apiName - Optional API name to filter tools
|
|
62
|
+
* @returns List of available tools
|
|
63
|
+
*/
|
|
64
|
+
listTools(apiName?: string): OCPTool[];
|
|
65
|
+
/**
|
|
66
|
+
* Get specific tool by name.
|
|
67
|
+
*
|
|
68
|
+
* @param toolName - Name of the tool to find
|
|
69
|
+
* @param apiName - Optional API name to search within
|
|
70
|
+
* @returns Found tool or undefined
|
|
71
|
+
*/
|
|
72
|
+
getTool(toolName: string, apiName?: string): OCPTool | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* Search tools by name or description.
|
|
75
|
+
*
|
|
76
|
+
* @param query - Search query
|
|
77
|
+
* @param apiName - Optional API name to search within
|
|
78
|
+
* @returns List of matching tools
|
|
79
|
+
*/
|
|
80
|
+
searchTools(query: string, apiName?: string): OCPTool[];
|
|
81
|
+
/**
|
|
82
|
+
* Call a discovered tool with OCP context injection.
|
|
83
|
+
*
|
|
84
|
+
* @param toolName - Name of the tool to call
|
|
85
|
+
* @param parameters - Parameters for the tool call
|
|
86
|
+
* @param apiName - Optional API name if tool name is ambiguous
|
|
87
|
+
* @param headers - Optional headers for this specific request (overrides registered headers)
|
|
88
|
+
* @returns HTTP response from the API call
|
|
89
|
+
*/
|
|
90
|
+
callTool(toolName: string, parameters?: Record<string, any>, apiName?: string, headers?: Record<string, string>): Promise<OCPResponse>;
|
|
91
|
+
/**
|
|
92
|
+
* Validate parameters against tool schema.
|
|
93
|
+
*/
|
|
94
|
+
private _validateParameters;
|
|
95
|
+
/**
|
|
96
|
+
* Build HTTP request from tool and parameters.
|
|
97
|
+
*/
|
|
98
|
+
private _buildRequest;
|
|
99
|
+
/**
|
|
100
|
+
* Get documentation for a specific tool.
|
|
101
|
+
*/
|
|
102
|
+
getToolDocumentation(toolName: string, apiName?: string): string;
|
|
103
|
+
/**
|
|
104
|
+
* Update agent goal and context.
|
|
105
|
+
*/
|
|
106
|
+
updateGoal(goal: string, summary?: string): void;
|
|
107
|
+
/**
|
|
108
|
+
* Normalize API name for case-insensitive matching.
|
|
109
|
+
*/
|
|
110
|
+
private _normalizeApiName;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/agent.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAQ1C;;;;;;;;GAQG;AACH,qBAAa,QAAQ;IACjB,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,QAAQ,EAAE,WAAW,CAAC;IACtB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACvC,UAAU,EAAE,aAAa,CAAC;IAC1B,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IAE3B;;;;;;;;;OASG;gBAEC,SAAS,GAAE,MAA2B,EACtC,IAAI,CAAC,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,EACpB,WAAW,GAAE,OAAc;IAgB/B;;;;;;;;;;;;;;;;OAgBG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAqE1H;;;;;OAKG;IACH,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE;IAmBtC;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAYhE;;;;;;OAMG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE;IAmBvD;;;;;;;;OAQG;IACG,QAAQ,CACV,QAAQ,EAAE,MAAM,EAChB,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EACpC,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC,OAAO,CAAC,WAAW,CAAC;IAyFvB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA8B3B;;OAEG;IACH,OAAO,CAAC,aAAa;IAwDrB;;OAEG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM;IAShE;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAIhD;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAG5B"}
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OCP Agent - Context-Aware API Discovery and Execution
|
|
3
|
+
*
|
|
4
|
+
* Combines OCP's context management with automatic API discovery,
|
|
5
|
+
* providing intelligent API interactions with zero infrastructure.
|
|
6
|
+
*/
|
|
7
|
+
import { AgentContext } from './context.js';
|
|
8
|
+
import { OCPSchemaDiscovery } from './schema_discovery.js';
|
|
9
|
+
import { OCPHTTPClient } from './http_client.js';
|
|
10
|
+
import { OCPRegistry } from './registry.js';
|
|
11
|
+
import { OCPStorage } from './storage.js';
|
|
12
|
+
const DEFAULT_AGENT_TYPE = 'ai_agent';
|
|
13
|
+
const DEFAULT_CACHE_MAX_AGE_DAYS = 7;
|
|
14
|
+
const DEFAULT_REQUEST_TIMEOUT = 30000;
|
|
15
|
+
const CACHE_SOURCE_NAME = 'cache';
|
|
16
|
+
const REGISTRY_SOURCE_PREFIX = 'registry';
|
|
17
|
+
/**
|
|
18
|
+
* OCP Agent for Context-Aware API Interactions
|
|
19
|
+
*
|
|
20
|
+
* Provides:
|
|
21
|
+
* 1. API Discovery (from OpenAPI specs or community registry)
|
|
22
|
+
* 2. Tool Invocation (with parameter validation)
|
|
23
|
+
* 3. Context Management (persistent across calls)
|
|
24
|
+
* 4. Zero Infrastructure (no servers required)
|
|
25
|
+
*/
|
|
26
|
+
export class OCPAgent {
|
|
27
|
+
/**
|
|
28
|
+
* Initialize OCP Agent with context and schema discovery.
|
|
29
|
+
*
|
|
30
|
+
* @param agentType - Type of AI agent (e.g., "ide_coding_assistant")
|
|
31
|
+
* @param user - User identifier
|
|
32
|
+
* @param workspace - Current workspace/project
|
|
33
|
+
* @param agentGoal - Current objective or goal
|
|
34
|
+
* @param registryUrl - Custom registry URL (uses OCP_REGISTRY_URL env var or default if not provided)
|
|
35
|
+
* @param enableCache - Enable local API caching and session persistence (default: true)
|
|
36
|
+
*/
|
|
37
|
+
constructor(agentType = DEFAULT_AGENT_TYPE, user, workspace, agentGoal, registryUrl, enableCache = true) {
|
|
38
|
+
this.context = new AgentContext({
|
|
39
|
+
agent_type: agentType,
|
|
40
|
+
user,
|
|
41
|
+
workspace,
|
|
42
|
+
current_goal: agentGoal
|
|
43
|
+
});
|
|
44
|
+
this.discovery = new OCPSchemaDiscovery();
|
|
45
|
+
this.registry = new OCPRegistry(registryUrl);
|
|
46
|
+
this.knownApis = new Map();
|
|
47
|
+
this.apiClients = new Map();
|
|
48
|
+
this.httpClient = new OCPHTTPClient(this.context);
|
|
49
|
+
this.storage = enableCache ? new OCPStorage() : null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Register an API for discovery and usage.
|
|
53
|
+
*
|
|
54
|
+
* @param name - Human-readable name for the API or registry API name
|
|
55
|
+
* @param specUrl - URL to OpenAPI specification (optional if using registry lookup)
|
|
56
|
+
* @param baseUrl - Optional override for API base URL
|
|
57
|
+
* @param headers - Optional headers for authenticated requests to this API
|
|
58
|
+
* @returns Discovered API specification with available tools
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* // Registry lookup (fast)
|
|
62
|
+
* await agent.registerApi('github');
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* // Direct OpenAPI discovery
|
|
66
|
+
* await agent.registerApi('my-api', 'https://api.example.com/openapi.json');
|
|
67
|
+
*/
|
|
68
|
+
async registerApi(name, specUrl, baseUrl, headers) {
|
|
69
|
+
// Normalize name for case-insensitive matching
|
|
70
|
+
name = this._normalizeApiName(name);
|
|
71
|
+
// Check memory first
|
|
72
|
+
const existingSpec = this.knownApis.get(name);
|
|
73
|
+
if (existingSpec) {
|
|
74
|
+
return existingSpec;
|
|
75
|
+
}
|
|
76
|
+
// Check cache if storage enabled (7-day expiration)
|
|
77
|
+
if (this.storage) {
|
|
78
|
+
const cachedSpec = await this.storage.getCachedApi(name, DEFAULT_CACHE_MAX_AGE_DAYS);
|
|
79
|
+
if (cachedSpec) {
|
|
80
|
+
this.knownApis.set(name, cachedSpec);
|
|
81
|
+
this.context.addApiSpec(name, CACHE_SOURCE_NAME);
|
|
82
|
+
return cachedSpec;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Lookup chain: Registry or Direct OpenAPI discovery
|
|
86
|
+
let apiSpec;
|
|
87
|
+
let source;
|
|
88
|
+
if (specUrl) {
|
|
89
|
+
// Direct OpenAPI discovery (existing behavior)
|
|
90
|
+
apiSpec = await this.discovery.discoverApi(specUrl, baseUrl);
|
|
91
|
+
source = specUrl;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Registry lookup (new behavior)
|
|
95
|
+
apiSpec = await this.registry.getApiSpec(name, baseUrl);
|
|
96
|
+
source = `${REGISTRY_SOURCE_PREFIX}:${name}`;
|
|
97
|
+
}
|
|
98
|
+
// Store API spec in memory and set name
|
|
99
|
+
apiSpec.name = name;
|
|
100
|
+
this.knownApis.set(name, apiSpec);
|
|
101
|
+
// Create wrapped client if headers provided
|
|
102
|
+
if (headers) {
|
|
103
|
+
const { _wrapApi } = await import('./http_client.js');
|
|
104
|
+
this.apiClients.set(name, _wrapApi(apiSpec.base_url, this.context, headers));
|
|
105
|
+
}
|
|
106
|
+
// Cache to disk if storage enabled
|
|
107
|
+
if (this.storage) {
|
|
108
|
+
await this.storage.cacheApi(name, apiSpec, { source });
|
|
109
|
+
}
|
|
110
|
+
// Add to context's API specs
|
|
111
|
+
this.context.addApiSpec(name, source);
|
|
112
|
+
// Log API registration
|
|
113
|
+
this.context.addInteraction('api_registered', source, `Discovered ${apiSpec.tools.length} tools`, {
|
|
114
|
+
api_name: name,
|
|
115
|
+
api_title: apiSpec.title,
|
|
116
|
+
tool_count: apiSpec.tools.length,
|
|
117
|
+
base_url: apiSpec.base_url,
|
|
118
|
+
source: specUrl ? 'openapi' : 'registry'
|
|
119
|
+
});
|
|
120
|
+
return apiSpec;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* List available tools.
|
|
124
|
+
*
|
|
125
|
+
* @param apiName - Optional API name to filter tools
|
|
126
|
+
* @returns List of available tools
|
|
127
|
+
*/
|
|
128
|
+
listTools(apiName) {
|
|
129
|
+
if (apiName) {
|
|
130
|
+
apiName = this._normalizeApiName(apiName);
|
|
131
|
+
const apiSpec = this.knownApis.get(apiName);
|
|
132
|
+
if (!apiSpec) {
|
|
133
|
+
throw new Error(`Unknown API: ${apiName}`);
|
|
134
|
+
}
|
|
135
|
+
return apiSpec.tools;
|
|
136
|
+
}
|
|
137
|
+
// Return tools from all APIs
|
|
138
|
+
const allTools = [];
|
|
139
|
+
for (const apiSpec of this.knownApis.values()) {
|
|
140
|
+
allTools.push(...apiSpec.tools);
|
|
141
|
+
}
|
|
142
|
+
return allTools;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get specific tool by name.
|
|
146
|
+
*
|
|
147
|
+
* @param toolName - Name of the tool to find
|
|
148
|
+
* @param apiName - Optional API name to search within
|
|
149
|
+
* @returns Found tool or undefined
|
|
150
|
+
*/
|
|
151
|
+
getTool(toolName, apiName) {
|
|
152
|
+
const tools = this.listTools(apiName);
|
|
153
|
+
for (const tool of tools) {
|
|
154
|
+
if (tool.name === toolName) {
|
|
155
|
+
return tool;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Search tools by name or description.
|
|
162
|
+
*
|
|
163
|
+
* @param query - Search query
|
|
164
|
+
* @param apiName - Optional API name to search within
|
|
165
|
+
* @returns List of matching tools
|
|
166
|
+
*/
|
|
167
|
+
searchTools(query, apiName) {
|
|
168
|
+
if (apiName) {
|
|
169
|
+
apiName = this._normalizeApiName(apiName);
|
|
170
|
+
const apiSpec = this.knownApis.get(apiName);
|
|
171
|
+
if (!apiSpec) {
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
return this.discovery.searchTools(apiSpec, query);
|
|
175
|
+
}
|
|
176
|
+
// Search across all APIs
|
|
177
|
+
const matches = [];
|
|
178
|
+
for (const apiSpec of this.knownApis.values()) {
|
|
179
|
+
matches.push(...this.discovery.searchTools(apiSpec, query));
|
|
180
|
+
}
|
|
181
|
+
return matches;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Call a discovered tool with OCP context injection.
|
|
185
|
+
*
|
|
186
|
+
* @param toolName - Name of the tool to call
|
|
187
|
+
* @param parameters - Parameters for the tool call
|
|
188
|
+
* @param apiName - Optional API name if tool name is ambiguous
|
|
189
|
+
* @param headers - Optional headers for this specific request (overrides registered headers)
|
|
190
|
+
* @returns HTTP response from the API call
|
|
191
|
+
*/
|
|
192
|
+
async callTool(toolName, parameters = {}, apiName, headers) {
|
|
193
|
+
// Find the tool
|
|
194
|
+
const tool = this.getTool(toolName, apiName);
|
|
195
|
+
if (!tool) {
|
|
196
|
+
const availableTools = this.listTools(apiName).map(t => t.name);
|
|
197
|
+
throw new Error(`Tool '${toolName}' not found. Available tools: ${availableTools.join(', ')}`);
|
|
198
|
+
}
|
|
199
|
+
// Find the API spec for this tool
|
|
200
|
+
let apiSpec;
|
|
201
|
+
for (const spec of this.knownApis.values()) {
|
|
202
|
+
if (spec.tools.includes(tool)) {
|
|
203
|
+
apiSpec = spec;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (!apiSpec) {
|
|
208
|
+
throw new Error(`Could not find API spec for tool '${toolName}'`);
|
|
209
|
+
}
|
|
210
|
+
// Validate parameters
|
|
211
|
+
const validationErrors = this._validateParameters(tool, parameters);
|
|
212
|
+
if (validationErrors.length > 0) {
|
|
213
|
+
throw new Error(`Parameter validation failed: ${validationErrors.join(', ')}`);
|
|
214
|
+
}
|
|
215
|
+
// Determine HTTP client to use (priority: call_tool headers > registered headers > default)
|
|
216
|
+
let client;
|
|
217
|
+
if (headers) {
|
|
218
|
+
// Per-call override: create temporary wrapped client
|
|
219
|
+
const { _wrapApi } = await import('./http_client.js');
|
|
220
|
+
client = _wrapApi(apiSpec.base_url, this.context, headers);
|
|
221
|
+
}
|
|
222
|
+
else if (apiSpec.name && this.apiClients.has(apiSpec.name)) {
|
|
223
|
+
// Use registered client with headers
|
|
224
|
+
client = this.apiClients.get(apiSpec.name);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
// Use default client (no auth)
|
|
228
|
+
client = this.httpClient;
|
|
229
|
+
}
|
|
230
|
+
// Build request
|
|
231
|
+
const [url, requestParams] = this._buildRequest(apiSpec, tool, parameters);
|
|
232
|
+
// Log the tool call
|
|
233
|
+
this.context.addInteraction(`tool_call:${toolName}`, url, 'executing', {
|
|
234
|
+
tool_name: toolName,
|
|
235
|
+
parameters,
|
|
236
|
+
method: tool.method
|
|
237
|
+
});
|
|
238
|
+
// Make the request with OCP context enhancement
|
|
239
|
+
try {
|
|
240
|
+
const response = await client.request(tool.method, url, requestParams);
|
|
241
|
+
// Log the result
|
|
242
|
+
this.context.addInteraction(`tool_response:${toolName}`, url, `${response.status} ${response.statusText}`, {
|
|
243
|
+
status_code: response.status,
|
|
244
|
+
success: response.ok,
|
|
245
|
+
response_size: response.text.length
|
|
246
|
+
});
|
|
247
|
+
return response;
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
// Log the error
|
|
251
|
+
this.context.addInteraction(`tool_error:${toolName}`, url, `Error: ${error instanceof Error ? error.message : String(error)}`, {
|
|
252
|
+
error_type: error instanceof Error ? error.constructor.name : 'Unknown',
|
|
253
|
+
error_message: error instanceof Error ? error.message : String(error)
|
|
254
|
+
});
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Validate parameters against tool schema.
|
|
260
|
+
*/
|
|
261
|
+
_validateParameters(tool, parameters) {
|
|
262
|
+
const errors = [];
|
|
263
|
+
// Check required parameters
|
|
264
|
+
for (const [paramName, paramInfo] of Object.entries(tool.parameters)) {
|
|
265
|
+
if (paramInfo.required && !(paramName in parameters)) {
|
|
266
|
+
errors.push(`Missing required parameter: ${paramName}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Check parameter types (basic validation)
|
|
270
|
+
for (const [paramName, value] of Object.entries(parameters)) {
|
|
271
|
+
if (paramName in tool.parameters) {
|
|
272
|
+
const paramInfo = tool.parameters[paramName];
|
|
273
|
+
const expectedType = paramInfo.type || 'string';
|
|
274
|
+
// Basic type checking
|
|
275
|
+
if (expectedType === 'integer' && !Number.isInteger(value)) {
|
|
276
|
+
errors.push(`Parameter '${paramName}' should be integer, got ${typeof value}`);
|
|
277
|
+
}
|
|
278
|
+
else if (expectedType === 'boolean' && typeof value !== 'boolean') {
|
|
279
|
+
errors.push(`Parameter '${paramName}' should be boolean, got ${typeof value}`);
|
|
280
|
+
}
|
|
281
|
+
else if (expectedType === 'array' && !Array.isArray(value)) {
|
|
282
|
+
errors.push(`Parameter '${paramName}' should be array, got ${typeof value}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return errors;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Build HTTP request from tool and parameters.
|
|
290
|
+
*/
|
|
291
|
+
_buildRequest(apiSpec, tool, parameters) {
|
|
292
|
+
// Start with base URL and path
|
|
293
|
+
let url = apiSpec.base_url.replace(/\/$/, '') + tool.path;
|
|
294
|
+
// Separate parameters by location
|
|
295
|
+
const pathParams = {};
|
|
296
|
+
const queryParams = {};
|
|
297
|
+
const bodyParams = {};
|
|
298
|
+
const headerParams = {};
|
|
299
|
+
for (const [paramName, value] of Object.entries(parameters)) {
|
|
300
|
+
if (paramName in tool.parameters) {
|
|
301
|
+
const location = tool.parameters[paramName].location || 'query';
|
|
302
|
+
if (location === 'path') {
|
|
303
|
+
pathParams[paramName] = value;
|
|
304
|
+
}
|
|
305
|
+
else if (location === 'query') {
|
|
306
|
+
queryParams[paramName] = value;
|
|
307
|
+
}
|
|
308
|
+
else if (location === 'body') {
|
|
309
|
+
bodyParams[paramName] = value;
|
|
310
|
+
}
|
|
311
|
+
else if (location === 'header') {
|
|
312
|
+
headerParams[paramName] = value;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// Replace path parameters
|
|
317
|
+
for (const [paramName, value] of Object.entries(pathParams)) {
|
|
318
|
+
url = url.replace(`{${paramName}}`, String(value));
|
|
319
|
+
}
|
|
320
|
+
// Build request parameters
|
|
321
|
+
const requestParams = {};
|
|
322
|
+
if (Object.keys(queryParams).length > 0) {
|
|
323
|
+
requestParams.params = queryParams;
|
|
324
|
+
}
|
|
325
|
+
if (Object.keys(bodyParams).length > 0) {
|
|
326
|
+
requestParams.json = bodyParams;
|
|
327
|
+
}
|
|
328
|
+
if (Object.keys(headerParams).length > 0) {
|
|
329
|
+
requestParams.headers = headerParams;
|
|
330
|
+
}
|
|
331
|
+
// Set timeout
|
|
332
|
+
requestParams.timeout = 30000;
|
|
333
|
+
return [url, requestParams];
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Get documentation for a specific tool.
|
|
337
|
+
*/
|
|
338
|
+
getToolDocumentation(toolName, apiName) {
|
|
339
|
+
const tool = this.getTool(toolName, apiName);
|
|
340
|
+
if (!tool) {
|
|
341
|
+
return `Tool '${toolName}' not found`;
|
|
342
|
+
}
|
|
343
|
+
return this.discovery.generateToolDocumentation(tool);
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Update agent goal and context.
|
|
347
|
+
*/
|
|
348
|
+
updateGoal(goal, summary) {
|
|
349
|
+
this.context.updateGoal(goal, summary);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Normalize API name for case-insensitive matching.
|
|
353
|
+
*/
|
|
354
|
+
_normalizeApiName(name) {
|
|
355
|
+
return name.toLowerCase().trim();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
//# sourceMappingURL=agent.js.map
|