Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
844d5b8
feat(owletto-backend): ClientSDK + sandbox scaffolding for execute/se…
buremba Apr 24, 2026
e896b51
fix(owletto-backend): audit ClientSDK against handler schemas + conso…
buremba Apr 24, 2026
0b43ac1
fix(owletto-backend): remaining namespace contract drift + serial int…
buremba Apr 24, 2026
5fd50c2
fix(owletto-backend): authProfiles.create uses `slug` (not `auth_prof…
buremba Apr 24, 2026
2b87829
chore(owletto-backend): drop unused env arg from organizations namespace
buremba Apr 24, 2026
aec6b26
docs(plans): land multi-org execute + search plan alongside PR-1 scaf…
buremba Apr 24, 2026
0523b49
chore(owletto-backend): remove dead getCurrentOrgInfo helper
buremba Apr 24, 2026
440cb31
fix(deps): regenerate bun.lock with packages/owletto-web submodule in…
buremba Apr 24, 2026
4f73e7c
fix(owletto-backend): escape backslashes in typebox-to-signature lite…
buremba Apr 24, 2026
e2dd143
feat(owletto-backend): wire execute + search MCP tools, swap reaction…
buremba Apr 24, 2026
9dfd372
fix(owletto-backend): drop legacy MCP registrations + reaction `react…
buremba Apr 24, 2026
e5614f4
fix(owletto-backend): address self + Pi review findings on PR #348
buremba Apr 25, 2026
5c1a40f
chore(docs): align stragglers with the post-cold-drop MCP surface
buremba Apr 25, 2026
e513aeb
fix(owletto-backend): enforce SDK action access
buremba Apr 25, 2026
a81ecd6
fix(ci): run owletto auth test with bun
buremba Apr 25, 2026
004582a
fix(ci): build owletto sdk before backend tests
buremba Apr 25, 2026
dc089f7
fix(owletto-backend): apply pi second-pass review findings
buremba Apr 25, 2026
23988d7
fix(owletto-backend): preserve internal admin REST tools
buremba Apr 25, 2026
532723a
fix(owletto-backend): keep manage_connections + manage_auth_profiles …
buremba Apr 25, 2026
800ae22
Merge remote-tracking branch 'origin/main' into feat/mcp-sandbox-scaf…
buremba Apr 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,16 @@ jobs:
- name: Build core package first
run: cd packages/core && bun run build

- name: Build owletto SDK for backend tests
run: cd packages/owletto-sdk && bun run build

- name: Run tests with coverage
run: |
# Run core, gateway, and cli tests fully
bun test packages/core packages/gateway packages/cli --coverage
# Owletto backend sandbox/auth coverage for MCP execute/search changes
bun test packages/owletto-backend/src/__tests__/unit/sandbox
bun test packages/owletto-backend/src/auth/__tests__/tool-access.test.ts
# Worker tests that don't transitively load pi-coding-agent runtime (WASM unavailable on CI)
bun test packages/worker/src/__tests__/embedded-tools.test.ts packages/worker/src/__tests__/model-resolver.test.ts packages/worker/src/__tests__/tool-policy.test.ts packages/worker/src/__tests__/processor.test.ts packages/worker/src/__tests__/audio-provider-suggestions.test.ts packages/worker/src/__tests__/generated-media.test.ts packages/worker/src/__tests__/tool-implementations.test.ts packages/worker/src/__tests__/instructions.test.ts packages/worker/src/__tests__/custom-tools.test.ts

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ packages/**/*.d.ts.map
!packages/cli/bin/*.js
# Tailwind config is a JS config file, not a build artifact
!packages/gateway/tailwind.config.js
# Hand-authored type shims for optional native modules (not build output)
!packages/**/src/types/*.d.ts
tsconfig.tsbuildinfo
**/tsconfig.tsbuildinfo

Expand Down
97 changes: 14 additions & 83 deletions bun.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions docker/app/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ FROM node:22-slim AS builder
WORKDIR /app

# Bun for workspace install + tsc. git needed because some deps ship git URLs.
# python3 + build-essential needed for isolated-vm native build (node-gyp).
RUN apt-get update && apt-get install -y --no-install-recommends \
git ca-certificates curl unzip \
python3 build-essential \
&& rm -rf /var/lib/apt/lists/* \
&& curl -fsSL https://bun.sh/install | BUN_INSTALL=/usr/local bash \
&& chmod +x /usr/local/bin/bun
Expand Down
332 changes: 332 additions & 0 deletions docs/plans/mcp-multi-org-and-execute.md

Large diffs are not rendered by default.

17 changes: 10 additions & 7 deletions packages/landing/src/content/docs/reference/owletto-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,14 @@ npx owletto@latest run
# Search knowledge
npx owletto@latest run search_knowledge '{"query":"Acme"}'

# Read saved content
npx owletto@latest run read_knowledge '{"query":"customer preferences"}'

# Save new knowledge
npx owletto@latest run save_knowledge '{"content":"Prefers weekly summaries","semantic_type":"preference","metadata":{}}'

# Discover SDK methods (namespaces, signatures, examples)
npx owletto@latest run search '{"query":"watchers.create"}'

# Run a TypeScript script over the typed client SDK
npx owletto@latest run execute '{"script":"export default async (ctx, client) => client.entities.list({ entity_type: \"company\", limit: 5 })"}'
```

This is the most direct way to inspect or test Owletto behavior outside an agent runtime.
Expand All @@ -151,13 +154,13 @@ This is the most direct way to inspect or test Owletto behavior outside an agent

The exact tool list depends on the endpoint and your session scope. Run `owletto run` with no arguments to see what is available.

**Core memory:** `search_knowledge`, `read_knowledge`, `save_knowledge`
**Core memory:** `search_knowledge`, `save_knowledge`

**Watchers:** `list_watchers`, `get_watcher`
**SDK surface:** `search` (method discovery), `execute` (run TS over the typed `ClientSDK` — replaces the previous `manage_*` MCP tools; reach handlers via `client.<namespace>.<method>(...)` from inside the script)

**Organization:** `list_organizations`, `switch_organization` (unscoped endpoint only)
**Read-only SQL:** `query_sql` (admin/owner only)

**Admin / workspace** (admin sessions only): `manage_entity`, `manage_entity_schema`, `manage_connections`, `manage_feeds`, `manage_auth_profiles`, `manage_operations`, `manage_watchers`, `manage_classifiers`, `query_sql`
**Organization:** `list_organizations`, `switch_organization` (exposed on both unscoped and scoped endpoints)

## Other Useful Commands

Expand Down
5 changes: 3 additions & 2 deletions packages/owletto-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@
},
"dependencies": {
"@hono/node-server": "^1.13.7",
"@jitl/quickjs-ng-wasmfile-release-asyncify": "0.31.0",
"@lobu/core": "workspace:*",
"@lobu/gateway": "workspace:*",
"@lobu/owletto-sdk": "workspace:*",
"@modelcontextprotocol/sdk": "^1.27.1",
"@polyglot-sql/sdk": "^0.1.13",
"@react-email/components": "^1.0.12",
"@react-email/render": "^2.0.7",
"@sebastianwessel/quickjs": "^3.0.1",
"@sentry/node": "^9.0.0",
"@sinclair/typebox": "^0.34.41",
"@slack/socket-mode": "^2.0.6",
Expand Down Expand Up @@ -58,5 +56,8 @@
"typescript": "^5.7.2",
"vite": "^6.0.0",
"vitest": "^2.1.8"
},
"optionalDependencies": {
"isolated-vm": "^5.0.4"
}
}
49 changes: 26 additions & 23 deletions packages/owletto-backend/src/__tests__/integration/mcp/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,17 +262,15 @@ describe('MCP Authentication', () => {
const body = await response.json();
const toolNames = body.result.tools.map((tool: any) => tool.name);

// Public reads survive: search_knowledge, search (SDK discovery).
expect(toolNames).toContain('search_knowledge');
expect(toolNames).toContain('search');
// Writes and admin reads must not be visible to anonymous public callers.
expect(toolNames).not.toContain('save_knowledge');
expect(toolNames).not.toContain('query_sql');

const manageEntity = body.result.tools.find((tool: any) => tool.name === 'manage_entity');
expect(manageEntity).toBeDefined();
expect(manageEntity.inputSchema.properties.action.enum).toEqual([
'list',
'get',
'list_links',
]);
expect(toolNames).not.toContain('execute');
// Legacy `manage_*` tools are no longer registered as external MCP tools.
expect(toolNames).not.toContain('manage_entity');
});

it('allows anonymous public-read tool calls on public org MCP routes', async () => {
Expand Down Expand Up @@ -379,16 +377,11 @@ describe('MCP Authentication', () => {
const toolNames = body.result.tools.map((tool: any) => tool.name);

expect(toolNames).toContain('search_knowledge');
expect(toolNames).toContain('search');
expect(toolNames).not.toContain('save_knowledge');
expect(toolNames).not.toContain('query_sql');

const manageEntity = body.result.tools.find((tool: any) => tool.name === 'manage_entity');
expect(manageEntity).toBeDefined();
expect(manageEntity.inputSchema.properties.action.enum).toEqual([
'list',
'get',
'list_links',
]);
expect(toolNames).not.toContain('execute');
expect(toolNames).not.toContain('manage_entity');
});

it('should reject expired OAuth access token', async () => {
Expand Down Expand Up @@ -730,7 +723,10 @@ describe('MCP Authentication', () => {
expect(body.error?.message).toContain("Agent 'missing-agent' was not found");
});

it('hides org switching tools on scoped /mcp/:org routes', async () => {
it('exposes org switching tools on scoped /mcp/:org routes too', async () => {
// PR-2: list_organizations + switch_organization are no longer
// gated behind the unscoped /mcp endpoint. Scoped sessions can still
// call switch_organization to move off the URL pin.
const { token } = await createTestAccessToken(user.id, org.id, client.client_id);

const response = await post(`/mcp/${org.slug}`, {
Expand All @@ -747,8 +743,8 @@ describe('MCP Authentication', () => {
const body = await response.json();
const toolNames = body.result.tools.map((tool: any) => tool.name);

expect(toolNames).not.toContain('list_organizations');
expect(toolNames).not.toContain('switch_organization');
expect(toolNames).toContain('list_organizations');
expect(toolNames).toContain('switch_organization');
});
});

Expand Down Expand Up @@ -924,12 +920,19 @@ describe('MCP Authentication', () => {

expect(result.tools).toBeInstanceOf(Array);

// Verify expected tools are present
// Verify expected tools are present. The legacy `manage_*`,
// `read_knowledge`, `get_watcher`, `list_watchers` MCP tools are now
// internal-only and reachable via the SDK from `execute` scripts.
const toolNames = result.tools.map((t: any) => t.name);
expect(toolNames).toContain('search_knowledge');
expect(toolNames).toContain('read_knowledge');
expect(toolNames).toContain('get_watcher');
expect(toolNames).toContain('list_watchers');
expect(toolNames).toContain('save_knowledge');
expect(toolNames).toContain('search');
expect(toolNames).toContain('execute');
expect(toolNames).not.toContain('read_knowledge');
expect(toolNames).not.toContain('get_watcher');
expect(toolNames).not.toContain('list_watchers');
expect(toolNames).not.toContain('manage_entity');
expect(toolNames).not.toContain('join_organization');
});

it('should include tool descriptions', async () => {
Expand Down
Loading
Loading