Skip to content

Commit d5c47bd

Browse files
authored
Merge pull request #26 from BjornMelin/feat/agent-registry-research-mcp
feat: agent registry research mcp + tools
2 parents b89319a + 11ba2f4 commit d5c47bd

112 files changed

Lines changed: 11129 additions & 365 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/dependabot.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ updates:
1010
# Keep typings aligned with Node LTS (we target Node 24.x).
1111
- dependency-name: "@types/node"
1212
update-types: ["version-update:semver-major"]
13+
# ESLint v10 is not yet supported by our lint plugin stack (jsdoc/tsdoc +
14+
# typescript-eslint utils). Keep us on v9 until upstream support lands.
15+
- dependency-name: "eslint"
16+
update-types: ["version-update:semver-major"]
17+
versions: [">=10.0.0"]
18+
- dependency-name: "@eslint/js"
19+
update-types: ["version-update:semver-major"]
20+
versions: [">=10.0.0"]
1321
groups:
1422
dependencies:
1523
patterns:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ build/
2929

3030
# Coverage
3131
coverage/
32+
coverage_tmp_*/
3233
*.lcov
3334

3435
# Logs

bun.lock

Lines changed: 85 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/architecture/adr/ADR-0008-web-research-exa-firecrawl-with-citations.md

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,41 @@
11
---
22
ADR: 0008
33
Title: Web research: Exa + Firecrawl with citations
4-
Status: Accepted
5-
Version: 0.2
6-
Date: 2026-01-30
4+
Status: Implemented
5+
Version: 0.4
6+
Date: 2026-02-07
77
Supersedes: []
88
Superseded-by: []
99
Related: [ADR-0006, ADR-0013]
1010
Tags: [research, architecture]
1111
References:
12-
- [Exa tool registry](https://ai-sdk.dev/tools-registry/exa)
13-
- [Firecrawl tool registry](https://ai-sdk.dev/tools-registry/firecrawl)
12+
- [Exa Search API](https://docs.exa.ai/reference/search)
13+
- [Firecrawl Node SDK](https://docs.firecrawl.dev/sdks/node)
14+
- [OWASP SSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
1415
---
1516

1617
## Status
1718

18-
Accepted — 2026-01-30.
19+
Implemented — 2026-02-07.
1920

2021
## Description
2122

22-
Use Exa for search and Firecrawl for page extraction, enforcing citation capture per claim.
23+
Use Exa for search and Firecrawl for page extraction, enforcing citation capture per claim with strict budgets, caching, and SSRF guardrails.
2324

2425
## Context
2526

26-
Market validation and competitive research require up-to-date web information. We need reliable search results plus robust extraction to get clean content for summarization. Both Exa and Firecrawl have AI SDK tools, reducing custom integration code.
27+
Market validation and competitive research require up-to-date web information. We need reliable search results plus robust extraction to get clean content for summarization.
2728

2829
## Decision Drivers
2930

3031
- High-quality search
3132
- Reliable extraction
3233
- Citation enforcement
33-
- AI SDK tool ecosystem
34+
- Low maintenance surface area (thin wrappers + caching)
3435

3536
## Alternatives
3637

37-
- A: Exa + Firecrawl — Pros: purpose-built; AI SDK tools. Cons: two vendors.
38+
- A: Exa + Firecrawl — Pros: purpose-built; thin, typed adapters. Cons: two vendors.
3839
- B: Only search (no extraction) — Pros: simpler. Cons: low quality grounding.
3940
- C: Scrape manually — Pros: control. Cons: high maintenance and fragility.
4041

@@ -53,11 +54,15 @@ Market validation and competitive research require up-to-date web information. W
5354

5455
We will adopt **Exa** for search and **Firecrawl** for content extraction, capturing source URLs and minimal excerpts for citations.
5556

57+
Implementation detail: Exa calls use a small REST wrapper (AbortController + timeouts) rather than the Exa SDK to guarantee deterministic cancellation under load.[^exa-search]
58+
5659
## Constraints
5760

5861
- Respect robots and provider terms.
5962
- Do not store full copyrighted articles; store snippets + summaries.
6063
- Enforce max URLs per step and cache results.
64+
- Enforce SSRF guardrails for outbound extraction URLs.[^owasp-ssrf]
65+
- SSRF validation is defense-in-depth and intentionally does not perform DNS resolution; hostnames that resolve to private IPs or DNS rebinding attacks are out of scope and must be mitigated via provider-side protections or egress controls.
6166

6267
## High-Level Architecture
6368

@@ -92,14 +97,17 @@ flowchart LR
9297

9398
### Architecture Overview
9499

95-
- `src/lib/ai/tools/web-search.ts` wraps Exa queries.
96-
- `src/lib/ai/tools/firecrawl.ts` wraps Firecrawl extract.
97-
- Cache by `(url, extractProfile)` in Upstash Redis.
100+
- `src/lib/ai/tools/web-search.server.ts` wraps Exa queries.
101+
- `src/lib/ai/tools/web-extract.server.ts` wraps Firecrawl extraction.
102+
- `src/lib/net/fetch-with-timeout.server.ts` enforces AbortController timeouts for upstream requests.
103+
- `src/lib/security/url-safety.server.ts` blocks known-unsafe URLs (SSRF defense-in-depth).
104+
- Cache by `(tool, params)` in Upstash Redis.
98105

99106
### Implementation Details
100107

101108
- Store citations as `{url, title, publishedAt?, accessedAt, excerpt}`.
102-
- Render citations in UI as footnotes per message/section.
109+
- Link citations to artifacts (not chat messages) for auditing/export.
110+
- Render citations in artifact markdown via `citation:n` links.
103111

104112
## Testing
105113

@@ -131,9 +139,14 @@ flowchart LR
131139

132140
### Dependencies
133141

134-
- **Added**: @exa/ai-sdk, firecrawl-aisdk (or official package)
142+
- **Added**: `@mendable/firecrawl-js`
135143

136144
## Changelog
137145

138146
- **0.1 (2026-01-29)**: Initial version.
139147
- **0.2 (2026-01-30)**: Updated for current repo baseline (Bun, `src/` layout, CI).
148+
- **0.3 (2026-02-07)**: Implemented with server wrappers, caching, budgets, and artifact-linked citations.
149+
- **0.4 (2026-02-07)**: Hardened adapters with deterministic timeouts and SSRF guardrails; removed unused Exa SDK dependency.
150+
151+
[^exa-search]: See References: Exa Search API
152+
[^owasp-ssrf]: See References: OWASP SSRF Prevention Cheat Sheet

docs/architecture/adr/ADR-0012-mcp-dynamic-tools-context7-via-mcp-dynamictool.md

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
---
22
ADR: 0012
33
Title: MCP + dynamic tools: Context7 via MCP + dynamicTool
4-
Status: Accepted
5-
Version: 0.2
6-
Date: 2026-01-30
4+
Status: Implemented
5+
Version: 0.4
6+
Date: 2026-02-07
77
Supersedes: []
88
Superseded-by: []
99
Related: [ADR-0006, ADR-0008, ADR-0013]
@@ -16,7 +16,7 @@ References:
1616

1717
## Status
1818

19-
Accepted — 2026-01-30.
19+
Implemented — 2026-02-07.
2020

2121
## Description
2222

@@ -52,7 +52,7 @@ Library APIs change quickly. Instead of hardcoding doc content into prompts, we
5252

5353
## Decision
5454

55-
We will use **MCP tools** via `createMCPClient` and expose them to agents using `dynamicTool()` so only the required doc tools are in context for a given step.
55+
We will use **MCP tools** via `createMCPClient` and only expose them to agents when the selected agent mode allowlists them (default deny).
5656

5757
## Constraints
5858

@@ -64,8 +64,8 @@ We will use **MCP tools** via `createMCPClient` and expose them to agents using
6464

6565
```mermaid
6666
flowchart LR
67-
Agent --> Dynamic[dynamicTool()]
68-
Dynamic --> MCPClient[createMCPClient]
67+
Agent --> Toolset[Mode-scoped toolset]
68+
Toolset --> MCPClient[createMCPClient]
6969
MCPClient --> Context7[(Context7 MCP server)]
7070
```
7171

@@ -91,13 +91,18 @@ flowchart LR
9191

9292
### Architecture Overview
9393

94-
- MCP client configured as stdio transport.
94+
- MCP client configured as HTTP transport.
9595
- Tools: resolve library id, query docs.
9696

9797
### Implementation Details
9898

99-
- Add `src/lib/ai/tools/mcp.ts` that provides a `getMcpTools()` factory.
100-
- Use `dynamicTool()` to inject only when step requires.
99+
- `src/lib/ai/tools/mcp-context7.server.ts` wraps Context7 MCP tools (cached + size-bounded).
100+
- Tool calls are time-bounded and abortable:
101+
- Budget: `budgets.context7TimeoutMs` in `src/lib/config/budgets.server.ts`
102+
- Enforcement: AbortController timeout in `src/lib/ai/tools/mcp-context7.server.ts`
103+
- Propagation: `options.abortSignal` is passed from `ToolExecutionOptions.abortSignal` in `src/workflows/chat/steps/context7.step.ts`
104+
- `src/lib/ai/tools/factory.server.ts` injects Context7 tools only for allowlisted modes.
105+
- `src/workflows/chat/steps/context7.step.ts` enforces per-turn budgets for Context7 calls.
101106

102107
## Testing
103108

@@ -126,9 +131,11 @@ flowchart LR
126131

127132
### Dependencies
128133

129-
- **Added**: @modelcontextprotocol/sdk, context7 tools
134+
- **Added**: `@ai-sdk/mcp` (MCP client transport)
130135

131136
## Changelog
132137

133138
- **0.1 (2026-01-29)**: Initial version.
134139
- **0.2 (2026-01-30)**: Updated for current repo baseline (Bun, `src/` layout, CI).
140+
- **0.3 (2026-02-07)**: Implemented with mode-scoped tool injection, Redis caching, and budgets.
141+
- **0.4 (2026-02-07)**: Documented abortable, time-bounded MCP calls (`context7TimeoutMs`) and the workflow abort propagation path.

docs/architecture/adr/ADR-0013-caching-cost-controls-next-js-caching-upstash-redis-budgets.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
ADR: 0013
33
Title: Caching + cost controls: Next.js caching + Upstash Redis + budgets
44
Status: Implemented
5-
Version: 0.5
6-
Date: 2026-02-06
5+
Version: 0.6
6+
Date: 2026-02-07
77
Supersedes: []
88
Superseded-by: []
99
Related: [ADR-0004, ADR-0005, ADR-0007]
@@ -108,6 +108,10 @@ flowchart LR
108108

109109
- Define budgets in `src/lib/config/budgets.server.ts`.
110110
- Persist per-step usage, enforce max tokens and tool calls.
111+
- Enforce upstream timeouts for external providers to avoid hung requests:
112+
- Exa search: `src/lib/ai/tools/web-search.server.ts` via `src/lib/net/fetch-with-timeout.server.ts` using `budgets.webSearchTimeoutMs`.
113+
- Firecrawl extraction: `src/lib/ai/tools/web-extract.server.ts` using `budgets.webExtractTimeoutMs`.
114+
- Context7 MCP: `src/lib/ai/tools/mcp-context7.server.ts` using `budgets.context7TimeoutMs`.
111115
- Standardize cache tags in `src/lib/cache/tags.ts` and apply to server loaders
112116
using `'use cache'` + `cacheTag`.
113117
- Apply server-side search rate limiting with Upstash Ratelimit in
@@ -161,3 +165,4 @@ flowchart LR
161165
- **0.3 (2026-02-03)**: Linked to SPEC-0021 as the cross-cutting finalization spec.
162166
- **0.4 (2026-02-06)**: Marked implemented; added Cache Components/tag invalidation implementation details and concrete file references.
163167
- **0.5 (2026-02-06)**: Added ownership-scoped search + Upstash Ratelimit implementation details for `/api/search`.
168+
- **0.6 (2026-02-07)**: Documented upstream timeouts as part of budgets and cost controls for external tool calls.

docs/architecture/spec/SPEC-0004-chat-retrieval-augmentation.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
22
spec: SPEC-0004
33
title: Chat + retrieval augmentation
4-
version: 0.3.0
5-
date: 2026-02-03
4+
version: 0.4.0
5+
date: 2026-02-07
66
owners: ["Bjorn Melin"]
7-
status: Proposed
7+
status: Implemented
88
related_requirements: ["FR-008", "FR-009", "FR-019", "PR-001", "PR-002"]
99
related_adrs: ["ADR-0006", "ADR-0004", "ADR-0011"]
1010
notes: "Defines chat UX, persistence, and RAG behavior for grounded responses."
@@ -106,6 +106,9 @@ Requirement IDs are defined in [docs/specs/requirements.md](/docs/specs/requirem
106106

107107
- `src/app/(app)/projects/[projectId]/chat/page.tsx`: UI for streaming chat and message history.
108108
- `src/app/api/chat/route.ts`: server streaming endpoint; must persist messages/tool calls.
109+
- `src/app/api/chat/[runId]/route.ts`: resume endpoint for durable chat sessions.
110+
- `src/lib/data/chat.server.ts`: chat threads + messages DAL (Neon + Drizzle).
111+
- `src/workflows/chat/project-chat.workflow.ts`: durable chat workflow orchestration and persistence.
109112
- `src/lib/ai/tools/retrieval.server.ts`: retrieval tools (uploads + artifacts);
110113
must enforce project scoping and top-k bounds.
111114
- `src/lib/upstash/vector.server.ts`: vector query interface with metadata filters.
@@ -121,7 +124,7 @@ Requirement IDs are defined in [docs/specs/requirements.md](/docs/specs/requirem
121124

122125
- Chat streams responses and persists message history
123126
- RAG tool returns cited sources for grounded answers
124-
- Agent modes can be switched per message
127+
- Agent mode is selected per thread and persisted; tool allowlists follow mode
125128

126129
## Testing
127130

docs/architecture/spec/SPEC-0006-agent-registry-orchestration-patterns.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
---
22
spec: SPEC-0006
33
title: Agent registry & orchestration patterns
4-
version: 0.3.0
5-
date: 2026-02-01
4+
version: 0.4.0
5+
date: 2026-02-07
66
owners: ["Bjorn Melin"]
7-
status: Proposed
7+
status: Implemented
88
related_requirements: ["FR-009", "FR-033", "NFR-011", "NFR-013", "NFR-015"]
99
related_adrs: ["ADR-0006", "ADR-0012", "ADR-0024"]
10-
notes: "Defines agent modes, tool allowlists, and orchestration patterns."
10+
notes: "Defines agent modes, tool allowlists, and orchestration patterns (implemented for project chat + research)."
1111
---
1212

1313
## Summary
@@ -18,9 +18,8 @@ Defines:
1818
- which tools each mode can access (least privilege)
1919
- orchestration patterns for multi-agent workflows
2020

21-
The system uses AI SDK v6 agents and dynamic tools to reduce context bloat
22-
(see [AI SDK Agents](https://ai-sdk.dev/docs/agents/overview) and
23-
[dynamicTool](https://ai-sdk.dev/docs/reference/ai-sdk-core/dynamic-tool)).
21+
The system uses AI SDK v6 agents and tool allowlisting to reduce context bloat
22+
(see [AI SDK Agents](https://ai-sdk.dev/docs/agents/overview)).
2423

2524
## Context
2625

@@ -87,15 +86,18 @@ Requirement IDs are defined in [docs/specs/requirements.md](/docs/specs/requirem
8786

8887
### File-level contracts
8988

90-
- `src/lib/ai/agents/*`: agent definitions (ToolLoopAgent configs) per mode.
91-
- `src/lib/ai/tools/*`: tool implementations and wrappers (typed schemas).
92-
- `src/lib/ai/registry.ts`: canonical registry mapping mode → agent/tools.
89+
- `src/lib/ai/agents/agent-mode.ts`: shared agent mode types.
90+
- `src/lib/ai/agents/modes/*`: mode definitions (system prompt + model + budgets + tool allowlist).
91+
- `src/lib/ai/agents/registry.ts`: canonical registry mapping modeId → mode definition.
92+
- `src/lib/ai/agents/registry.server.ts`: server-facing registry (env gates + enabled modes).
93+
- `src/lib/ai/tools/tool-ids.ts`: canonical tool IDs for allowlists.
94+
- `src/lib/ai/tools/factory.server.ts`: tool factory enforcing allowlists (default deny).
9395

9496
### Configuration
9597

9698
- Tool allowlists must be enforced at agent construction time (not by “prompt
9799
instructions” alone).
98-
- Dynamic tools (MCP/Context7) must be sandboxed/guarded and bounded by budgets.
100+
- Dynamic tools (MCP/Context7) must be guarded and bounded by budgets.
99101

100102
## Agent modes
101103

0 commit comments

Comments
 (0)