@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/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
+ };