Add MCP_TOOL to backend BlockType enum and frontend BlockUIType enum,
matching the existing pattern used by AGENT blocks. Replace all
SpecialBlockID.MCP_TOOL type-checks with uiType-based checks.
When a configured credential is deleted, set input_data to None (consistent
with the "no credentials" path at line 279) instead of the field's raw
default value ({}), which would fail CredentialsMetaInput validation.
Replace fastapi.testclient.TestClient with httpx.AsyncClient + ASGITransport.
TestClient creates a new anyio blocking portal per request. When 11+ portals
are created and destroyed in a session that also has pytest-asyncio session-scoped
async fixtures, the session event loop gets corrupted, causing
"RuntimeError: Event loop is closed" in subsequent async tests.
AsyncClient with ASGITransport runs the ASGI app directly in the current
event loop without creating blocking portals.
Drop the broad `except Exception` catch-and-reraise-as-HTTPException
blocks. Keep only the meaningful error handlers (HTTPClientError for
401/403, MCPClientError for 502). Unhandled exceptions now propagate
naturally to FastAPI's default 500 handler.
The MCP conftest.py with pytest hooks (pytest_addoption,
pytest_collection_modifyitems) was disrupting pytest-asyncio's session
event loop lifecycle, causing the SpinTestServer to be torn down before
session-scoped oauth tests could run.
Replace the conftest-based e2e gating with a simple pytestmark skipif
in the test file itself.
The entire merged input_data dict (containing server_url, credentials,
selected_tool, etc.) was being assigned to tool_arguments instead of
just the tool_arguments sub-dict. This would cause validation failures
or MCP server rejections.
### Changes
- Removed `defaultExpanded` prop from `ToolAccordion` in CreateAgent,
EditAgent, RunAgent, and RunBlock components to streamline the code and
improve readability.
### Impact
- This refactor enhances maintainability by reducing complexity in the
component structure while preserving existing functionality.
### Changes 🏗️
- Removed conditional expansion logic from all tool components
- Simplified ToolAccordion implementation across all affected components
### Checklist 📋
#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
- [x] Create and run agents with various tools to verify accordion
behavior works correctly
- [x] Verify that UI components expand and collapse as expected
- [x] Test with different output types to ensure proper rendering
---------
Co-authored-by: Ubbe <hi@ubbe.dev>
Co-authored-by: Lluis Agusti <hi@llu.lu>
The MCP conftest.py was overriding session-scoped `server` and
`graph_cleanup` fixtures with no-op versions. Having two session-scoped
fixtures with the same name at different directory levels caused
pytest-asyncio event loop conflicts, making all oauth_test.py tests
fail with "Event loop is closed".
Since these fixtures are session-scoped and shared across the entire
test run, the override was unnecessary — the SpinTestServer is already
created for other tests.
Also adds defensive `access_token` key validation in MCP OAuth token
exchange and refresh to prevent KeyError on malformed responses.
## Summary
Enables Anthropic's extended thinking feature for Claude models in
CoPilot via OpenRouter. This keeps the model's chain-of-thought
reasoning internal rather than outputting it to users.
## Problem
The CoPilot prompt was designed for a thinking agent (with
`<internal_reasoning>` tags), but extended thinking wasn't enabled on
the API side. This caused the model to output its reasoning as regular
text, leaking internal analysis to users.
## Solution
Added thinking configuration to the OpenRouter `extra_body` for
Anthropic models:
```python
extra_body["provider"] = {
"anthropic": {
"thinking": {
"type": "enabled",
"budget_tokens": config.thinking_budget_tokens,
}
}
}
```
## Configuration
New settings in `ChatConfig`:
| Setting | Default | Description |
|---------|---------|-------------|
| `thinking_enabled` | `True` | Enable extended thinking for Claude
models |
| `thinking_budget_tokens` | `10000` | Token budget for thinking
(1000-100000) |
## Changes
- `config.py`: Added `thinking_enabled` and `thinking_budget_tokens`
settings
- `service.py`: Added thinking config to all 3 places where `extra_body`
is built for LLM calls
## Testing
- Verify CoPilot responses no longer include internal reasoning text
- Check that Claude's extended thinking is working (should see thinking
tokens in usage)
- Confirm non-Anthropic models are unaffected
## Related
Discussion:
https://discord.com/channels/1126875755960336515/1126875756925046928/1470779843552612607
---------
Co-authored-by: Swifty <craigswift13@gmail.com>
These "is the user authenticated, and should they be?" checks should not
be spread across the codebase, it's complex enough as it is. :')
- Follow-up to #12050
### Changes 🏗️
- Revert "fix(frontend): copilot redirect logout (#12050)"
- Add `/copilot` to `PROTECTED_PAGES` in `@/lib/supabase/helpers`
### Checklist 📋
#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
- [x] Trivial change, we know this works for other pages
The MCP conftest's sync server/graph_cleanup fixtures must match the
parent conftest's async pytest_asyncio fixtures to avoid disrupting
the session event loop management, which caused "Event loop is closed"
errors in oauth_test.py tests.
The server and graph_cleanup fixtures in conftest.py require explicit
pytest_asyncio.fixture(loop_scope="session") to properly manage the
session event loop. Using plain pytest.fixture causes "Event loop is
closed" errors in all oauth_test.py tests.
Restores session-scoped fixtures and pytest_asyncio decorators that were
accidentally changed, causing "RuntimeError: Event loop is closed" in
test_authorize_creates_code_in_database. Also regenerates openapi.json.
Bumps the production-dependencies group in /autogpt_platform/backend
with 2 updates: [fastapi](https://github.com/fastapi/fastapi) and
[langfuse](https://github.com/langfuse/langfuse).
Updates `fastapi` from 0.128.5 to 0.128.6
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/fastapi/fastapi/releases">fastapi's
releases</a>.</em></p>
<blockquote>
<h2>0.128.6</h2>
<h3>Fixes</h3>
<ul>
<li>🐛 Fix <code>on_startup</code> and <code>on_shutdown</code>
parameters of <code>APIRouter</code>. PR <a
href="https://redirect.github.com/fastapi/fastapi/pull/14873">#14873</a>
by <a
href="https://github.com/YuriiMotov"><code>@YuriiMotov</code></a>.</li>
</ul>
<h3>Translations</h3>
<ul>
<li>🌐 Update translations for zh (update-outdated). PR <a
href="https://redirect.github.com/fastapi/fastapi/pull/14843">#14843</a>
by <a
href="https://github.com/tiangolo"><code>@tiangolo</code></a>.</li>
</ul>
<h3>Internal</h3>
<ul>
<li>✅ Fix parameterized tests with snapshots. PR <a
href="https://redirect.github.com/fastapi/fastapi/pull/14875">#14875</a>
by <a
href="https://github.com/YuriiMotov"><code>@YuriiMotov</code></a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fbca586c1d"><code>fbca586</code></a>
📝 Update release notes</li>
<li><a
href="4e879799dd"><code>4e87979</code></a>
📝 Update release notes</li>
<li><a
href="0a4033aeee"><code>0a4033a</code></a>
🔖 Release version 0.128.6</li>
<li><a
href="ed2512a5ec"><code>ed2512a</code></a>
🐛 Fix <code>on_startup</code> and <code>on_shutdown</code> parameters of
<code>APIRouter</code> (<a
href="https://redirect.github.com/fastapi/fastapi/issues/14873">#14873</a>)</li>
<li><a
href="0c0f6332e2"><code>0c0f633</code></a>
📝 Update release notes</li>
<li><a
href="227cb85a03"><code>227cb85</code></a>
✅ Fix parameterized tests with snapshots (<a
href="https://redirect.github.com/fastapi/fastapi/issues/14875">#14875</a>)</li>
<li><a
href="cd31576d57"><code>cd31576</code></a>
📝 Update release notes</li>
<li><a
href="376e108580"><code>376e108</code></a>
🌐 Update translations for zh (update-outdated) (<a
href="https://redirect.github.com/fastapi/fastapi/issues/14843">#14843</a>)</li>
<li>See full diff in <a
href="https://github.com/fastapi/fastapi/compare/0.128.5...0.128.6">compare
view</a></li>
</ul>
</details>
<br />
Updates `langfuse` from 3.13.0 to 3.14.1
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/langfuse/langfuse/commits">compare
view</a></li>
</ul>
</details>
<br />
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
</details>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Zamil Majdy <zamil.majdy@agpt.co>
Co-authored-by: Otto <otto@agpt.co>
## Changes 🏗️
Redirect to `/login` if the user is not authenticated and tries to
access `/copilot`
### Checklist 📋
#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
- [x] Run the app locally and tested
### Changes 🏗️
- Added AI SDK integration for chat streaming with proper message
handling
- Implemented custom to_sse method in StreamToolOutputAvailable to
exclude non-spec fields
- Modified stream_chat_completion to reuse message IDs for tool call
continuations
- Created new Copilot 2.0 UI with AI SDK React components
- Added streamdown and related packages for markdown rendering
- Built reusable conversation and message components for the chat
interface
- Added support for tool output display in the chat UI
### Checklist 📋
#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
- [x] Start a new chat session and verify streaming works correctly
- [x] Test tool calls and verify they display properly in the UI
- [x] Verify message continuations don't create duplicate messages
- [x] Test markdown rendering with code blocks and other formatting
- [x] Verify the UI is responsive and scrolls correctly
#### For configuration changes:
- [x] `.env.default` is updated or already compatible with my changes
- [x] `docker-compose.yml` is updated or already compatible with my
changes
- [x] I have included a list of my configuration changes in the PR
description (under **Changes**)
---------
Co-authored-by: Lluis Agusti <hi@llu.lu>
Co-authored-by: Ubbe <hi@ubbe.dev>
When a credential is deleted but the node still references its ID,
CredentialsSelect now treats the stale ID as unselected and falls
back to the first available credential instead of showing the raw ID.
MCP credentials use per-server dynamic OAuth handlers, not a static
handler registered in HANDLERS_BY_NAME. The delete endpoint now
creates a dynamic handler from credential metadata for token
revocation instead of failing with "Provider 'mcp' does not support
OAuth".
- Validate token_endpoint in OAuth metadata before accessing it
- Check authorization_servers list is non-empty before indexing
- Use provider_matches() (renamed from private _provider_matches) in
creds_manager for Python 3.13 StrEnum compatibility
- Fill in MCP block documentation with technical explanation and use cases
- Set customized_name in metadata when MCP and Agent blocks are created
(both legacy and new builder) so titles persist through save/load
- Remove convoluted agent_name fallback from NodeHeader and getNodeTitle
- Add custom block-level validation in graph pre-run checks so MCP tool
arguments are validated before execution
- Fix server_name fallback to URL hostname in discover_tools endpoint
Backend: Add required-field validation in MCPToolBlock.run() before
calling the MCP server. The executor-level validation is bypassed for
MCP blocks because get_input_defaults() flattens tool_arguments,
stripping tool_input_schema from the validation context.
Frontend: NodeHeader now derives the MCP server label from the server
URL hostname when server_name is missing (pruned by pruneEmptyValues).
This fixes the title for existing blocks that don't have customized_name
in metadata.
When the MCP server returns a null server_name, fall back to the URL
hostname so customized_name is always set in metadata. This prevents
the title from degrading to "MCP:" after save and reload.
- CredentialsSelect: default to first available credential instead of
"None" when credentials exist, reorder options to show credentials
before the "None" option, and notify parent on auto-select
- Revert CredentialsGroupedView user auto-select effect (now handled
at the CredentialsSelect level)
- Block.tsx: persist MCP block title as customized_name in metadata
so it survives save/load
Add auto-selection for user credentials (like MCP OAuth) in the
CredentialsGroupedView run dialog. When exactly one credential matches
the provider, type, and discriminator values (e.g. MCP server URL),
it is pre-selected instead of defaulting to "None (skip this credential)".
Frontend: Include credentials field in MCP block's dynamic input schema
so users can select OAuth credentials from the node form. Separate
credentials from tool_arguments in FormCreator to store them at the
correct level in hardcodedValues.
Backend: Add _auto_lookup_credential fallback in MCPToolBlock.run() for
legacy nodes that don't have credentials explicitly set. This resolves
the credential by matching mcp_server_url in stored OAuth metadata.
verify_state_token and get_creds_by_provider compared provider strings
with ==, which failed when OAuth states were stored with the buggy
"ProviderName.MCP" format from Python 3.13's str(Enum) behavior.
Also fix double-append in store_state_token where the state was written
once via edit_user_integrations and again via a redundant manual block.
- Add missing `refresh_if_needed` mock to test_discover_tools_auto_uses_stored_credential
so it returns the stored credential instead of a MagicMock
- Fix credential auto-unselect clearing MCP credentials on initial render:
skip the "unselect if not available" check when the saved credentials
list is empty (empty list means not loaded yet, not invalid)
- Fix pyright errors in graph_test.py by properly typing frozenset[CredentialsType]
- Fix executor validation crash when credentials is empty {} by nullifying
the field before JSON schema validation
- Exclude MCP Tool block from e2e block discovery test (requires dialog)
- Normalize provider string in CredentialsMetaResponse to handle Python 3.13
str(Enum) bug for stored credentials
- Fix get_host() to match MCP provider regardless of enum string format
The discover_tools endpoint was reading raw access tokens from stored
credentials without checking if they had expired. This caused users
to be prompted to re-authenticate every time the token expired (~1h).
Now uses creds_manager.refresh_if_needed() to transparently refresh
expired tokens before using them.
The pytest_asyncio fixture changes with loop_scope="session" caused
"Event loop is closed" errors in all 31 oauth_test.py tests on CI.
MCP tests have their own conftest override and don't need these changes.
The tests used MagicMock for block.input_schema but didn't mock
get_required_fields(), causing the "required missing creds" test to
silently treat all credentials as optional.
Adds diagnostic logging when the `type vector does not exist` error
occurs in raw SQL queries.
## Problem
We're seeing intermittent "type vector does not exist" errors on
dev-behave ([Sentry
issue](https://significant-gravitas.sentry.io/issues/7205929979/)). The
pgvector extension should be in the search_path, but occasionally
queries fail to resolve the vector type.
## Solution
When a query fails with this specific error, we now log:
- `SHOW search_path` - what schemas are being searched
- `SELECT current_schema()` - the active schema
- `SELECT current_user, session_user, current_database()` - connection
context
This diagnostic info will help identify why the vector extension isn't
visible in certain cases.
## Changes
- Added `_log_vector_error_diagnostics()` helper function in
`backend/data/db.py`
- Wrapped SQL execution in try/except to catch and diagnose vector type
errors
- Original exception is re-raised after logging (no behavior change)
## Testing
This is observational/diagnostic code. It will be validated by waiting
for the error to occur naturally on dev and checking the logs.
## Rollout
Once we've captured diagnostic logs and identified the root cause, this
logging can be removed or reduced in verbosity.
- Auto-select credential when exactly one match exists (even for
optional fields). Only skip auto-select for optional fields with
multiple choices.
- In executor, catch ValueError from creds_manager.acquire() for
optional credential fields — fall back to running without credentials
instead of crashing when stale IDs reference deleted credentials.
The sortByUnsetFirst comparator in splitCredentialFieldsBySystem
caused credential inputs to jump positions every time a credential
was selected (set fields moved to bottom, unset moved to top).
Remove the sort to keep stable ordering.
The model_validator on CredentialsMetaInput normalizes legacy
"ProviderName.MCP" format for Pydantic validation, but validate_data()
uses raw JSON schema which bypasses Pydantic. Write normalized values
back to input_data after Pydantic processes them so both validation
paths see correct data.
- Add model_validator on CredentialsMetaInput to auto-normalize old
"ProviderName.MCP" format to "mcp" at the model level, eliminating
the need for string parsing hacks in every consumer.
- Fix aggregate_credentials_inputs to check block schema defaults when
determining if credentials are required, not just node metadata.
MCP blocks with default={} are always optional regardless of metadata.
Also search for credentials stored with "ProviderName.MCP" (from the
Python 3.13 str(Enum) bug) during both discover-tools auto-lookup and
OAuth callback cleanup. Remove the temporary debug endpoint.