@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.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +198 -0
  3. package/dist/src/agent.d.ts +112 -0
  4. package/dist/src/agent.d.ts.map +1 -0
  5. package/dist/src/agent.js +358 -0
  6. package/dist/src/agent.js.map +1 -0
  7. package/dist/src/context.d.ts +108 -0
  8. package/dist/src/context.d.ts.map +1 -0
  9. package/dist/src/context.js +196 -0
  10. package/dist/src/context.js.map +1 -0
  11. package/dist/src/errors.d.ts +40 -0
  12. package/dist/src/errors.d.ts.map +1 -0
  13. package/dist/src/errors.js +63 -0
  14. package/dist/src/errors.js.map +1 -0
  15. package/dist/src/headers.d.ts +63 -0
  16. package/dist/src/headers.d.ts.map +1 -0
  17. package/dist/src/headers.js +238 -0
  18. package/dist/src/headers.js.map +1 -0
  19. package/dist/src/http_client.d.ts +82 -0
  20. package/dist/src/http_client.d.ts.map +1 -0
  21. package/dist/src/http_client.js +181 -0
  22. package/dist/src/http_client.js.map +1 -0
  23. package/dist/src/index.d.ts +25 -0
  24. package/dist/src/index.d.ts.map +1 -0
  25. package/dist/src/index.js +35 -0
  26. package/dist/src/index.js.map +1 -0
  27. package/dist/src/registry.d.ts +52 -0
  28. package/dist/src/registry.d.ts.map +1 -0
  29. package/dist/src/registry.js +164 -0
  30. package/dist/src/registry.js.map +1 -0
  31. package/dist/src/schema_discovery.d.ts +149 -0
  32. package/dist/src/schema_discovery.d.ts.map +1 -0
  33. package/dist/src/schema_discovery.js +707 -0
  34. package/dist/src/schema_discovery.js.map +1 -0
  35. package/dist/src/schemas/ocp-context.json +138 -0
  36. package/dist/src/storage.d.ts +110 -0
  37. package/dist/src/storage.d.ts.map +1 -0
  38. package/dist/src/storage.js +399 -0
  39. package/dist/src/storage.js.map +1 -0
  40. package/dist/src/validation.d.ts +169 -0
  41. package/dist/src/validation.d.ts.map +1 -0
  42. package/dist/src/validation.js +92 -0
  43. package/dist/src/validation.js.map +1 -0
  44. package/dist/tests/agent.test.d.ts +5 -0
  45. package/dist/tests/agent.test.d.ts.map +1 -0
  46. package/dist/tests/agent.test.js +536 -0
  47. package/dist/tests/agent.test.js.map +1 -0
  48. package/dist/tests/context.test.d.ts +5 -0
  49. package/dist/tests/context.test.d.ts.map +1 -0
  50. package/dist/tests/context.test.js +285 -0
  51. package/dist/tests/context.test.js.map +1 -0
  52. package/dist/tests/headers.test.d.ts +5 -0
  53. package/dist/tests/headers.test.d.ts.map +1 -0
  54. package/dist/tests/headers.test.js +356 -0
  55. package/dist/tests/headers.test.js.map +1 -0
  56. package/dist/tests/http_client.test.d.ts +5 -0
  57. package/dist/tests/http_client.test.d.ts.map +1 -0
  58. package/dist/tests/http_client.test.js +373 -0
  59. package/dist/tests/http_client.test.js.map +1 -0
  60. package/dist/tests/registry.test.d.ts +5 -0
  61. package/dist/tests/registry.test.d.ts.map +1 -0
  62. package/dist/tests/registry.test.js +232 -0
  63. package/dist/tests/registry.test.js.map +1 -0
  64. package/dist/tests/schema_discovery.test.d.ts +5 -0
  65. package/dist/tests/schema_discovery.test.d.ts.map +1 -0
  66. package/dist/tests/schema_discovery.test.js +1074 -0
  67. package/dist/tests/schema_discovery.test.js.map +1 -0
  68. package/dist/tests/storage.test.d.ts +5 -0
  69. package/dist/tests/storage.test.d.ts.map +1 -0
  70. package/dist/tests/storage.test.js +414 -0
  71. package/dist/tests/storage.test.js.map +1 -0
  72. package/dist/tests/validation.test.d.ts +5 -0
  73. package/dist/tests/validation.test.d.ts.map +1 -0
  74. package/dist/tests/validation.test.js +254 -0
  75. package/dist/tests/validation.test.js.map +1 -0
  76. 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