@barndoor-ai/sdk 0.2.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/.eslintignore +8 -0
- package/.eslintrc.cjs +102 -0
- package/.github/CODEOWNERS +4 -0
- package/.github/workflows/ci.yml +57 -0
- package/.prettierignore +6 -0
- package/.prettierrc +13 -0
- package/LICENSE +21 -0
- package/README.md +309 -0
- package/RELEASE.md +203 -0
- package/examples/README.md +92 -0
- package/examples/basic-mcp-client.js +134 -0
- package/examples/openai-integration.js +137 -0
- package/jest.config.js +16 -0
- package/openapi.yaml +681 -0
- package/package.json +87 -0
- package/rollup.config.js +63 -0
- package/scripts/dump-core-files.js +161 -0
- package/scripts/dump-typescript-only.js +150 -0
- package/src/auth/index.ts +26 -0
- package/src/auth/pkce.ts +346 -0
- package/src/auth/store.ts +809 -0
- package/src/client.ts +512 -0
- package/src/config.ts +402 -0
- package/src/exceptions/index.ts +205 -0
- package/src/http/client.ts +272 -0
- package/src/index.ts +92 -0
- package/src/logging.ts +111 -0
- package/src/models/index.ts +156 -0
- package/src/quickstart.ts +358 -0
- package/src/version.ts +41 -0
- package/test/client.test.js +381 -0
- package/test/config.test.js +202 -0
- package/test/exceptions.test.js +142 -0
- package/test/integration.test.js +147 -0
- package/test/models.test.js +177 -0
- package/test/token-management.test.js +81 -0
- package/test/token-validation.test.js +104 -0
- package/tsconfig.json +61 -0
package/RELEASE.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Release Process
|
|
2
|
+
|
|
3
|
+
This document is for maintainers and describes the process for releasing a new version of the Barndoor TypeScript SDK.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The SDK uses automated publishing via GitHub Actions. When you create a GitHub release, the CI/CD pipeline automatically:
|
|
8
|
+
1. Runs the full test suite
|
|
9
|
+
2. Builds the package
|
|
10
|
+
3. Publishes to npm
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
Before creating a release, ensure you have:
|
|
15
|
+
|
|
16
|
+
- [ ] Write access to the GitHub repository
|
|
17
|
+
- [ ] Permissions to create GitHub releases and merge pull requests
|
|
18
|
+
- [ ] All changes merged to the `main` branch
|
|
19
|
+
- [ ] All tests passing on `main`
|
|
20
|
+
|
|
21
|
+
## Release Steps
|
|
22
|
+
|
|
23
|
+
### 1. Prepare the Release Branch
|
|
24
|
+
|
|
25
|
+
Create a release branch from `main`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Ensure you're on main and up to date
|
|
29
|
+
git checkout main
|
|
30
|
+
git pull origin main
|
|
31
|
+
|
|
32
|
+
# Create a release branch
|
|
33
|
+
git checkout -b release/v0.1.1
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
#### Update Version Number
|
|
37
|
+
|
|
38
|
+
Update the version in `package.json`:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# For patch releases (0.1.0 -> 0.1.1)
|
|
42
|
+
npm version patch --no-git-tag-version
|
|
43
|
+
|
|
44
|
+
# For minor releases (0.1.0 -> 0.2.0)
|
|
45
|
+
npm version minor --no-git-tag-version
|
|
46
|
+
|
|
47
|
+
# For major releases (0.1.0 -> 1.0.0)
|
|
48
|
+
npm version major --no-git-tag-version
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Note:** We use `--no-git-tag-version` to prevent automatic tagging. The tag will be created after the PR is merged.
|
|
52
|
+
|
|
53
|
+
#### Verify the Build
|
|
54
|
+
|
|
55
|
+
Run the full build and test suite locally:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Clean build
|
|
59
|
+
npm run clean
|
|
60
|
+
npm install
|
|
61
|
+
|
|
62
|
+
# Build
|
|
63
|
+
npm run build
|
|
64
|
+
|
|
65
|
+
# Run all checks
|
|
66
|
+
npm test
|
|
67
|
+
npm run lint
|
|
68
|
+
npm run type-check
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
All checks should pass before proceeding.
|
|
72
|
+
|
|
73
|
+
#### Update Documentation (if needed)
|
|
74
|
+
|
|
75
|
+
- Update `README.md` if there are API changes
|
|
76
|
+
- Update examples if API usage has changed
|
|
77
|
+
|
|
78
|
+
#### Commit Version Changes
|
|
79
|
+
|
|
80
|
+
Commit the version bump and any documentation updates:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git add package.json package-lock.json README.md
|
|
84
|
+
git commit -m "chore: bump version to v0.1.1"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 2. Create and Merge Pull Request
|
|
88
|
+
|
|
89
|
+
#### Push Release Branch
|
|
90
|
+
|
|
91
|
+
Push the release branch to GitHub:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
git push origin release/v0.1.1
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Open Pull Request
|
|
98
|
+
|
|
99
|
+
1. Go to: https://github.com/barndoor-ai/barndoor-ts-sdk/pulls
|
|
100
|
+
2. Click "New pull request"
|
|
101
|
+
3. Set base to `main` and compare to `release/v0.1.1`
|
|
102
|
+
4. Title: "Release v0.1.1"
|
|
103
|
+
5. Description should include:
|
|
104
|
+
- Summary of changes
|
|
105
|
+
- Release notes
|
|
106
|
+
- Link to any relevant issues
|
|
107
|
+
6. Request reviews from team members if required
|
|
108
|
+
7. Wait for CI checks to pass
|
|
109
|
+
8. Get approval and merge the PR
|
|
110
|
+
|
|
111
|
+
### 3. Create and Push Release Tag
|
|
112
|
+
|
|
113
|
+
After the PR is merged, create the release tag:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Switch to main and pull the merged changes
|
|
117
|
+
git checkout main
|
|
118
|
+
git pull origin main
|
|
119
|
+
|
|
120
|
+
# Create the version tag
|
|
121
|
+
git tag v0.1.1
|
|
122
|
+
|
|
123
|
+
# Push the tag
|
|
124
|
+
git push origin v0.1.1
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 4. Create GitHub Release
|
|
128
|
+
|
|
129
|
+
#### Via GitHub Web UI
|
|
130
|
+
|
|
131
|
+
1. Go to: https://github.com/barndoor-ai/barndoor-ts-sdk/releases/new
|
|
132
|
+
2. Click "Choose a tag" and select the version tag you just pushed (e.g., `v0.1.1`)
|
|
133
|
+
3. Set the release title to the version number (e.g., `v0.1.1`)
|
|
134
|
+
4. Add release notes describing:
|
|
135
|
+
- **New Features**: What's new in this release
|
|
136
|
+
- **Bug Fixes**: What issues were resolved
|
|
137
|
+
- **Breaking Changes**: Any backwards-incompatible changes
|
|
138
|
+
- **Deprecations**: Features being deprecated
|
|
139
|
+
- **Internal Changes**: Refactoring, dependency updates, etc.
|
|
140
|
+
5. Check "Set as the latest release"
|
|
141
|
+
6. Click "Publish release"
|
|
142
|
+
|
|
143
|
+
### 5. Monitor the Release
|
|
144
|
+
|
|
145
|
+
Once you publish the GitHub release:
|
|
146
|
+
|
|
147
|
+
1. **Check GitHub Actions**: Go to https://github.com/barndoor-ai/barndoor-ts-sdk/actions
|
|
148
|
+
- The CI workflow should trigger automatically
|
|
149
|
+
- Verify the `build` job passes
|
|
150
|
+
- Verify the `publish` job completes successfully
|
|
151
|
+
|
|
152
|
+
2. **Verify npm Publication**: Check https://www.npmjs.com/package/@barndoor-ai/sdk
|
|
153
|
+
- The new version should appear within a few minutes
|
|
154
|
+
- Verify the package contents look correct
|
|
155
|
+
|
|
156
|
+
3. **Test Installation**: Install the published package:
|
|
157
|
+
```bash
|
|
158
|
+
# In a test directory
|
|
159
|
+
npm install @barndoor-ai/sdk@latest
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 6. Post-Release
|
|
163
|
+
|
|
164
|
+
#### Clean Up Release Branch
|
|
165
|
+
|
|
166
|
+
Once the release is successfully published, clean up the release branch:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Delete local branch
|
|
170
|
+
git branch -d release/v0.1.1
|
|
171
|
+
|
|
172
|
+
# Delete remote branch (if it wasn't auto-deleted by GitHub)
|
|
173
|
+
git push origin --delete release/v0.1.1
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Release Checklist
|
|
177
|
+
|
|
178
|
+
Use this checklist for each release:
|
|
179
|
+
|
|
180
|
+
- [ ] All changes merged to `main` branch
|
|
181
|
+
- [ ] Release branch created from latest `main`
|
|
182
|
+
- [ ] Version bumped in `package.json` (using `--no-git-tag-version`)
|
|
183
|
+
- [ ] Documentation updated for API changes
|
|
184
|
+
- [ ] Local build and tests pass
|
|
185
|
+
- [ ] Release PR opened and approved
|
|
186
|
+
- [ ] Release PR merged to `main`
|
|
187
|
+
- [ ] Release tag created and pushed
|
|
188
|
+
- [ ] GitHub release created with release notes
|
|
189
|
+
- [ ] CI/CD pipeline completed successfully
|
|
190
|
+
- [ ] Package verified on npm
|
|
191
|
+
- [ ] Test installation from npm works
|
|
192
|
+
|
|
193
|
+
## Emergency Rollback
|
|
194
|
+
|
|
195
|
+
If a critical issue is discovered after release:
|
|
196
|
+
|
|
197
|
+
1. Publish a new patch version with the fix immediately
|
|
198
|
+
2. Deprecate the broken version:
|
|
199
|
+
```bash
|
|
200
|
+
npm deprecate @barndoor-ai/sdk@0.1.1 "Critical bug - use 0.1.2+"
|
|
201
|
+
```
|
|
202
|
+
3. Notify all users through appropriate channels
|
|
203
|
+
4. Consider creating a security advisory if it's a security issue
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Barndoor SDK Examples
|
|
2
|
+
|
|
3
|
+
This directory contains working examples that demonstrate how to use the Barndoor TypeScript SDK with MCP servers.
|
|
4
|
+
|
|
5
|
+
## Working Examples
|
|
6
|
+
|
|
7
|
+
### 1. `openai-integration.js` - OpenAI + MCP Integration
|
|
8
|
+
Complete example showing how to integrate OpenAI's function calling with MCP tools.
|
|
9
|
+
|
|
10
|
+
**What it does:**
|
|
11
|
+
- Authenticates with Barndoor
|
|
12
|
+
- Connects to an MCP server (Salesforce by default)
|
|
13
|
+
- Lets OpenAI decide which MCP tools to call
|
|
14
|
+
- Executes the tools and feeds results back to OpenAI
|
|
15
|
+
|
|
16
|
+
**Requirements:**
|
|
17
|
+
```bash
|
|
18
|
+
npm install openai dotenv
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Environment:**
|
|
22
|
+
```bash
|
|
23
|
+
export OPENAI_API_KEY="your-openai-api-key"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Run:**
|
|
27
|
+
```bash
|
|
28
|
+
node examples/openai-integration.js
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 2. `basic-mcp-client.js` - Direct MCP Client
|
|
32
|
+
Basic example showing direct MCP server interaction without AI frameworks.
|
|
33
|
+
|
|
34
|
+
**What it does:**
|
|
35
|
+
- Authenticates with Barndoor
|
|
36
|
+
- Connects to an MCP server
|
|
37
|
+
- Lists available tools and resources
|
|
38
|
+
- Executes basic operations
|
|
39
|
+
|
|
40
|
+
**Requirements:**
|
|
41
|
+
```bash
|
|
42
|
+
npm install dotenv
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Run:**
|
|
46
|
+
```bash
|
|
47
|
+
node examples/basic-mcp-client.js
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
Both examples use the same authentication pattern:
|
|
53
|
+
|
|
54
|
+
1. **Environment Variables** (create a `.env` file):
|
|
55
|
+
```
|
|
56
|
+
AUTH_DOMAIN=your-auth-domain
|
|
57
|
+
AGENT_CLIENT_ID=your-client-id
|
|
58
|
+
AGENT_CLIENT_SECRET=your-client-secret
|
|
59
|
+
MODE=development # or production
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
2. **Server Configuration**: Change the `SERVER_SLUG` constant in each file to target different MCP servers:
|
|
63
|
+
- `'salesforce'` - Salesforce integration
|
|
64
|
+
- `'notion'` - Notion integration
|
|
65
|
+
- Or any other configured MCP server
|
|
66
|
+
|
|
67
|
+
## Key Patterns
|
|
68
|
+
|
|
69
|
+
Both examples follow the same proven pattern:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// 1. Authenticate
|
|
73
|
+
const sdk = await loginInteractive();
|
|
74
|
+
|
|
75
|
+
// 2. Ensure server is connected
|
|
76
|
+
await ensureServerConnected(sdk, SERVER_SLUG);
|
|
77
|
+
|
|
78
|
+
// 3. Create MCP client
|
|
79
|
+
const mcpClient = await makeMcpClient(sdk, SERVER_SLUG);
|
|
80
|
+
|
|
81
|
+
// 4. Use MCP client
|
|
82
|
+
const { tools } = await mcpClient.listTools();
|
|
83
|
+
const result = await mcpClient.callTool({ name: 'tool_name', arguments: {} });
|
|
84
|
+
|
|
85
|
+
// 5. Cleanup
|
|
86
|
+
await mcpClient.close();
|
|
87
|
+
await sdk.close();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This pattern uses the `@modelcontextprotocol/sdk` package internally and handles all the complex MCP protocol details for you.
|
|
91
|
+
|
|
92
|
+
**Note:** While the SDK is written in TypeScript, the current examples are still in JavaScript (.js files) for broader compatibility. You can easily convert them to TypeScript by changing the file extensions to `.ts` and adding type annotations as needed.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic MCP Client Example
|
|
3
|
+
*
|
|
4
|
+
* This script demonstrates how to use the Barndoor SDK to connect to MCP servers
|
|
5
|
+
* and perform basic operations without any AI framework dependencies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import 'dotenv/config';
|
|
9
|
+
import { loginInteractive, ensureServerConnected, makeMcpClient } from '../dist/index.esm.js';
|
|
10
|
+
|
|
11
|
+
const SERVER_SLUG = 'salesforce'; // or 'notion'
|
|
12
|
+
|
|
13
|
+
async function main() {
|
|
14
|
+
try {
|
|
15
|
+
console.log('š Starting Basic MCP Client Example...\n');
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------
|
|
18
|
+
// 1. Initialize Barndoor SDK and MCP client (with retry on 401)
|
|
19
|
+
// ---------------------------------------------------------------------
|
|
20
|
+
console.log('š Authenticating with Barndoor...');
|
|
21
|
+
let sdk = await loginInteractive();
|
|
22
|
+
|
|
23
|
+
// Test authentication by trying to list servers
|
|
24
|
+
try {
|
|
25
|
+
await sdk.listServers();
|
|
26
|
+
console.log('ā
Authentication successful!');
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error.statusCode === 401) {
|
|
29
|
+
console.log('ā ļø Cached token invalid, clearing and re-authenticating...');
|
|
30
|
+
// Clear cached token and try again
|
|
31
|
+
const { clearCachedToken } = await import('../dist/index.esm.js');
|
|
32
|
+
clearCachedToken();
|
|
33
|
+
sdk = await loginInteractive();
|
|
34
|
+
await sdk.listServers(); // Test again
|
|
35
|
+
console.log('ā
Re-authentication successful!');
|
|
36
|
+
} else {
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log(`\nš Ensuring ${SERVER_SLUG} server is connected...`);
|
|
42
|
+
await ensureServerConnected(sdk, SERVER_SLUG);
|
|
43
|
+
console.log(`ā
${SERVER_SLUG} server connected!`);
|
|
44
|
+
|
|
45
|
+
// Use the makeMcpClient helper
|
|
46
|
+
const mcpClient = await makeMcpClient(sdk, SERVER_SLUG);
|
|
47
|
+
console.log('š” MCP client connected!');
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------
|
|
50
|
+
// 2. List available resources and tools using SDK
|
|
51
|
+
// ---------------------------------------------------------------------
|
|
52
|
+
console.log('\nš Listing available resources...');
|
|
53
|
+
try {
|
|
54
|
+
const { resources = [] } = await mcpClient.listResources();
|
|
55
|
+
console.log(`Found ${resources.length} resources`);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.log('ā ļø List resources failed:', error.message);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log('\nš§ Listing available tools...');
|
|
61
|
+
try {
|
|
62
|
+
const { tools = [] } = await mcpClient.listTools();
|
|
63
|
+
console.log(`Found ${tools.length} tools`);
|
|
64
|
+
tools.forEach((tool, i) => {
|
|
65
|
+
console.log(` ${i + 1}. ${tool.name}: ${tool.description || 'No description'}`);
|
|
66
|
+
});
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.log('ā ļø List tools failed:', error.message);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------
|
|
72
|
+
// 3. Test specific operations
|
|
73
|
+
// ---------------------------------------------------------------------
|
|
74
|
+
console.log('\nš§Ŗ Testing specific operations...');
|
|
75
|
+
|
|
76
|
+
if (SERVER_SLUG === 'salesforce') {
|
|
77
|
+
console.log('\nš Testing Salesforce operations...');
|
|
78
|
+
try {
|
|
79
|
+
const queryResponse = await mcpClient.callTool({
|
|
80
|
+
name: 'query_records',
|
|
81
|
+
arguments: {
|
|
82
|
+
query: 'SELECT Id, Name, Industry FROM Account LIMIT 5',
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const records =
|
|
86
|
+
queryResponse?.content?.[0]?.type === 'text'
|
|
87
|
+
? JSON.parse(queryResponse.content[0].text)
|
|
88
|
+
: queryResponse.records || [];
|
|
89
|
+
console.log(`Found ${records.length} accounts`);
|
|
90
|
+
records.forEach((record, i) => {
|
|
91
|
+
console.log(` ${i + 1}. ${record.Name} (${record.Industry || 'No industry'})`);
|
|
92
|
+
});
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.log('ā ļø Salesforce query failed:', error.message);
|
|
95
|
+
}
|
|
96
|
+
} else if (SERVER_SLUG === 'notion') {
|
|
97
|
+
console.log('\nš Testing Notion operations...');
|
|
98
|
+
try {
|
|
99
|
+
const pagesResponse = await mcpClient.callTool({
|
|
100
|
+
name: 'list_pages',
|
|
101
|
+
arguments: {},
|
|
102
|
+
});
|
|
103
|
+
const pages = pagesResponse?.pages || [];
|
|
104
|
+
console.log(`Found ${pages.length} pages`);
|
|
105
|
+
pages.slice(0, 5).forEach((page, i) => {
|
|
106
|
+
console.log(` ${i + 1}. ${page.title || 'Untitled'}`);
|
|
107
|
+
});
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.log('ā ļø Notion operation failed:', error.message);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
await mcpClient.close();
|
|
114
|
+
await sdk.close();
|
|
115
|
+
console.log('\nā
Basic MCP client example completed successfully!');
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error('ā Error:', error.message);
|
|
118
|
+
if (error.stack) {
|
|
119
|
+
console.error('Stack trace:', error.stack);
|
|
120
|
+
}
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Handle unhandled promise rejections
|
|
126
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
127
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Run the main function
|
|
132
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
133
|
+
main();
|
|
134
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Function-Calling + MCP Integration
|
|
3
|
+
*
|
|
4
|
+
* Shows how to let an LLM decide which MCP tool to invoke, run it, and feed the
|
|
5
|
+
* result back to the model ā all in ~60 lines.
|
|
6
|
+
*
|
|
7
|
+
* Requirements:
|
|
8
|
+
* npm install openai dotenv
|
|
9
|
+
*
|
|
10
|
+
* Make sure OPENAI_API_KEY is set in .env.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import 'dotenv/config';
|
|
14
|
+
import { OpenAI } from 'openai';
|
|
15
|
+
import { loginInteractive, ensureServerConnected, makeMcpClient } from '../dist/index.esm.js';
|
|
16
|
+
|
|
17
|
+
const SERVER_SLUG = 'salesforce';
|
|
18
|
+
|
|
19
|
+
// --- Step 0: Prepare OpenAI client -------------------------------------------------
|
|
20
|
+
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
21
|
+
|
|
22
|
+
// --- Main --------------------------------------------------------------------------
|
|
23
|
+
(async () => {
|
|
24
|
+
let sdk;
|
|
25
|
+
let mcpClient;
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// 1. Authenticate and get MCP params (with retry on 401)
|
|
29
|
+
console.log('š Authenticating with Barndoor...');
|
|
30
|
+
sdk = await loginInteractive();
|
|
31
|
+
|
|
32
|
+
// Test authentication by trying to list servers
|
|
33
|
+
try {
|
|
34
|
+
await sdk.listServers();
|
|
35
|
+
console.log('ā
Authentication successful!');
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error.statusCode === 401) {
|
|
38
|
+
console.log('ā ļø Cached token invalid, clearing and re-authenticating...');
|
|
39
|
+
// Clear cached token and try again
|
|
40
|
+
const { clearCachedToken } = await import('../src/index.js');
|
|
41
|
+
clearCachedToken();
|
|
42
|
+
sdk = await loginInteractive();
|
|
43
|
+
await sdk.listServers(); // Test again
|
|
44
|
+
console.log('ā
Re-authentication successful!');
|
|
45
|
+
} else {
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await ensureServerConnected(sdk, SERVER_SLUG);
|
|
51
|
+
mcpClient = await makeMcpClient(sdk, SERVER_SLUG);
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------
|
|
54
|
+
// List available servers
|
|
55
|
+
// ---------------------------------------------------------------------
|
|
56
|
+
const servers = await sdk.listServers();
|
|
57
|
+
console.log('\nAvailable MCP servers:');
|
|
58
|
+
servers.forEach(s => console.log(` ⢠${s.slug.padEnd(12)} status=${s.connection_status}`));
|
|
59
|
+
|
|
60
|
+
// Fetch public URL as well for display
|
|
61
|
+
const [params, publicUrl] = (await sdk.makeMcpConnectionParams)
|
|
62
|
+
? await sdk.makeMcpConnectionParams(sdk, SERVER_SLUG)
|
|
63
|
+
: [null, null];
|
|
64
|
+
if (publicUrl) {
|
|
65
|
+
console.log(`\nā Ready ā MCP URL: ${params.url} (public: ${publicUrl})`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 2. Fetch available MCP tools and convert to OpenAI function schemas
|
|
69
|
+
const { tools = [] } = await mcpClient.listTools();
|
|
70
|
+
|
|
71
|
+
const functions = tools.map(t => ({
|
|
72
|
+
type: 'function',
|
|
73
|
+
function: {
|
|
74
|
+
name: t.name.replace(/[^A-Za-z0-9_]/g, '_').slice(0, 64), // ensure valid & short
|
|
75
|
+
description: t.description || 'MCP tool',
|
|
76
|
+
parameters: t.parameters ?? {
|
|
77
|
+
type: 'object',
|
|
78
|
+
properties: {},
|
|
79
|
+
additionalProperties: true,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
}));
|
|
83
|
+
|
|
84
|
+
// 3. Conversation loop ā ask the model, detect a function_call, run it, respond
|
|
85
|
+
// Example prompt (renamed to avoid semgrep false-positive on "user*")
|
|
86
|
+
const prompt = 'What are the first 5 Account names in Salesforce?';
|
|
87
|
+
const msgs = [{ role: 'user', content: prompt }];
|
|
88
|
+
|
|
89
|
+
// First round: model decides if it wants the function
|
|
90
|
+
let chat = await openai.chat.completions.create({
|
|
91
|
+
model: 'gpt-4o-mini',
|
|
92
|
+
messages: msgs,
|
|
93
|
+
tools: functions,
|
|
94
|
+
tool_choice: 'auto',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
let msg = chat.choices[0].message;
|
|
98
|
+
let toolCall = msg.tool_calls?.[0];
|
|
99
|
+
|
|
100
|
+
// Handle a single tool call for demo purposes
|
|
101
|
+
if (toolCall) {
|
|
102
|
+
const { name: toolName, arguments: raw } = toolCall.function;
|
|
103
|
+
const args = JSON.parse(raw || '{}');
|
|
104
|
+
|
|
105
|
+
const mcpResult = await mcpClient.callTool({
|
|
106
|
+
name: toolName,
|
|
107
|
+
arguments: args,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
msgs.push(msg); // add function_call msg
|
|
111
|
+
msgs.push({
|
|
112
|
+
role: 'tool',
|
|
113
|
+
tool_call_id: toolCall.id,
|
|
114
|
+
name: toolName,
|
|
115
|
+
content: JSON.stringify(mcpResult),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Second round: give result back to model for final answer
|
|
119
|
+
chat = await openai.chat.completions.create({
|
|
120
|
+
model: 'gpt-4o-mini',
|
|
121
|
+
messages: msgs,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log('\nš¬ Final answer:\n', chat.choices[0].message.content);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error('ā Error:', error.message);
|
|
128
|
+
if (error.stack) {
|
|
129
|
+
console.error('Stack trace:', error.stack);
|
|
130
|
+
}
|
|
131
|
+
process.exit(1);
|
|
132
|
+
} finally {
|
|
133
|
+
// Always cleanup
|
|
134
|
+
if (sdk) await sdk.close();
|
|
135
|
+
if (mcpClient) await mcpClient.close();
|
|
136
|
+
}
|
|
137
|
+
})();
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
testEnvironment: 'node',
|
|
3
|
+
transform: {},
|
|
4
|
+
collectCoverageFrom: [
|
|
5
|
+
'src/**/*.{ts,js}',
|
|
6
|
+
'!src/**/*.test.{ts,js}'
|
|
7
|
+
],
|
|
8
|
+
coverageThreshold: {
|
|
9
|
+
global: {
|
|
10
|
+
branches: 70,
|
|
11
|
+
functions: 70,
|
|
12
|
+
lines: 70,
|
|
13
|
+
statements: 70
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
};
|