universal-mcp 0.1.1__tar.gz → 0.1.2rc1__tar.gz
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.
- universal_mcp-0.1.2rc1/.github/workflows/publish-pypi.yml +48 -0
- universal_mcp-0.1.2rc1/.github/workflows/pull-request-checks.yml +8 -0
- universal_mcp-0.1.2rc1/.github/workflows/shared.yml +54 -0
- universal_mcp-0.1.2rc1/.gitignore +47 -0
- universal_mcp-0.1.2rc1/.pre-commit-config.yaml +24 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/PKG-INFO +22 -5
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/README.md +5 -1
- universal_mcp-0.1.2rc1/local_config.json.example +11 -0
- universal_mcp-0.1.2rc1/pyproject.toml +149 -0
- universal_mcp-0.1.2rc1/src/playground/README.md +65 -0
- universal_mcp-0.1.2rc1/src/playground/__main__.py +37 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/playground/agents/react.py +3 -2
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/playground/client.py +8 -8
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/playground/schema.py +2 -1
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/playground/service.py +9 -10
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/playground/settings.py +1 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/playground/streamlit.py +151 -19
- universal_mcp-0.1.2rc1/src/tests/test_api_generator.py +267 -0
- universal_mcp-0.1.2rc1/src/tests/test_applications.py +34 -0
- universal_mcp-0.1.2rc1/src/tests/test_localserver.py +30 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/tests/test_zenquotes.py +13 -10
- universal_mcp-0.1.2rc1/src/universal_mcp/applications/__init__.py +26 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/applications/application.py +13 -8
- universal_mcp-0.1.2rc1/src/universal_mcp/applications/e2b/app.py +74 -0
- universal_mcp-0.1.2rc1/src/universal_mcp/applications/firecrawl/app.py +381 -0
- universal_mcp-0.1.2rc1/src/universal_mcp/applications/github/README.md +35 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/applications/github/app.py +133 -100
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/applications/google_calendar/app.py +170 -139
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/applications/google_mail/app.py +185 -160
- universal_mcp-0.1.2rc1/src/universal_mcp/applications/markitdown/app.py +32 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/applications/reddit/app.py +112 -71
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/applications/resend/app.py +3 -8
- universal_mcp-0.1.2rc1/src/universal_mcp/applications/serp/app.py +84 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/applications/tavily/app.py +11 -10
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/applications/zenquotes/app.py +3 -3
- universal_mcp-0.1.2rc1/src/universal_mcp/cli.py +165 -0
- universal_mcp-0.1.2rc1/src/universal_mcp/config.py +32 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/exceptions.py +1 -3
- universal_mcp-0.1.2rc1/src/universal_mcp/integrations/__init__.py +8 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/integrations/agentr.py +26 -24
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/integrations/integration.py +72 -35
- universal_mcp-0.1.2rc1/src/universal_mcp/servers/__init__.py +23 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/servers/server.py +77 -44
- universal_mcp-0.1.2rc1/src/universal_mcp/stores/__init__.py +16 -0
- universal_mcp-0.1.2rc1/src/universal_mcp/stores/store.py +181 -0
- universal_mcp-0.1.2rc1/src/universal_mcp/utils/__init__.py +1 -0
- universal_mcp-0.1.2rc1/src/universal_mcp/utils/api_generator.py +269 -0
- universal_mcp-0.1.2rc1/src/universal_mcp/utils/docgen.py +360 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/utils/installation.py +17 -2
- universal_mcp-0.1.2rc1/src/universal_mcp/utils/openapi.py +372 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/uv.lock +1463 -87
- universal_mcp-0.1.1/.gitignore +0 -25
- universal_mcp-0.1.1/pyproject.toml +0 -37
- universal_mcp-0.1.1/src/tests/test_applications.py +0 -25
- universal_mcp-0.1.1/src/universal_mcp/applications/__init__.py +0 -31
- universal_mcp-0.1.1/src/universal_mcp/cli.py +0 -83
- universal_mcp-0.1.1/src/universal_mcp/config.py +0 -15
- universal_mcp-0.1.1/src/universal_mcp/integrations/__init__.py +0 -4
- universal_mcp-0.1.1/src/universal_mcp/servers/__init__.py +0 -3
- universal_mcp-0.1.1/src/universal_mcp/stores/__init__.py +0 -3
- universal_mcp-0.1.1/src/universal_mcp/stores/store.py +0 -71
- universal_mcp-0.1.1/src/universal_mcp/utils/openapi.py +0 -274
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/.python-version +0 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/CONTRIBUTING.md +0 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/playground/__init__.py +0 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/playground/memory/__init__.py +0 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/playground/memory/sqlite.py +0 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/playground/utils.py +0 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/tests/__init__.py +0 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/tests/conftest.py +0 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/__init__.py +0 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/integrations/README.md +0 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/py.typed +0 -0
- {universal_mcp-0.1.1 → universal_mcp-0.1.2rc1}/src/universal_mcp/utils/bridge.py +0 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
name: Publishing
|
2
|
+
|
3
|
+
on:
|
4
|
+
release:
|
5
|
+
types: [published]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
release-build:
|
9
|
+
name: Build distribution
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
needs: [checks]
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v4
|
14
|
+
|
15
|
+
- name: Install uv
|
16
|
+
uses: astral-sh/setup-uv@v3
|
17
|
+
with:
|
18
|
+
enable-cache: true
|
19
|
+
|
20
|
+
- name: Build
|
21
|
+
run: uv build
|
22
|
+
|
23
|
+
- name: Upload artifacts
|
24
|
+
uses: actions/upload-artifact@v4
|
25
|
+
with:
|
26
|
+
name: release-dists
|
27
|
+
path: dist/
|
28
|
+
|
29
|
+
checks:
|
30
|
+
uses: ./.github/workflows/shared.yml
|
31
|
+
|
32
|
+
pypi-publish:
|
33
|
+
name: Upload release to PyPI
|
34
|
+
runs-on: ubuntu-latest
|
35
|
+
needs:
|
36
|
+
- release-build
|
37
|
+
permissions:
|
38
|
+
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
39
|
+
|
40
|
+
steps:
|
41
|
+
- name: Retrieve release distributions
|
42
|
+
uses: actions/download-artifact@v4
|
43
|
+
with:
|
44
|
+
name: release-dists
|
45
|
+
path: dist/
|
46
|
+
|
47
|
+
- name: Publish package distributions to PyPI
|
48
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
@@ -0,0 +1,54 @@
|
|
1
|
+
name: Shared Checks
|
2
|
+
|
3
|
+
on:
|
4
|
+
workflow_call:
|
5
|
+
|
6
|
+
jobs:
|
7
|
+
format:
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
steps:
|
10
|
+
- uses: actions/checkout@v4
|
11
|
+
|
12
|
+
- name: Install uv
|
13
|
+
uses: astral-sh/setup-uv@v3
|
14
|
+
with:
|
15
|
+
enable-cache: true
|
16
|
+
|
17
|
+
- name: Install the project
|
18
|
+
run: uv sync --frozen --all-extras --dev
|
19
|
+
|
20
|
+
- name: Run ruff format check
|
21
|
+
run: uv run --no-sync ruff check .
|
22
|
+
|
23
|
+
typecheck:
|
24
|
+
runs-on: ubuntu-latest
|
25
|
+
steps:
|
26
|
+
- uses: actions/checkout@v4
|
27
|
+
|
28
|
+
- name: Install uv
|
29
|
+
uses: astral-sh/setup-uv@v3
|
30
|
+
with:
|
31
|
+
enable-cache: true
|
32
|
+
|
33
|
+
- name: Install the project
|
34
|
+
run: uv sync --frozen --all-extras --dev
|
35
|
+
|
36
|
+
# - name: Run pyright
|
37
|
+
# run: uv run --no-sync pyright
|
38
|
+
|
39
|
+
test:
|
40
|
+
runs-on: ubuntu-latest
|
41
|
+
|
42
|
+
steps:
|
43
|
+
- uses: actions/checkout@v4
|
44
|
+
|
45
|
+
- name: Install uv
|
46
|
+
uses: astral-sh/setup-uv@v3
|
47
|
+
with:
|
48
|
+
enable-cache: true
|
49
|
+
|
50
|
+
- name: Install the project
|
51
|
+
run: uv sync --frozen --all-extras --dev
|
52
|
+
|
53
|
+
- name: Run pytest
|
54
|
+
run: uv run --no-sync pytest
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Python-generated files
|
2
|
+
__pycache__/
|
3
|
+
*.py[oc]
|
4
|
+
build/
|
5
|
+
dist/
|
6
|
+
wheels/
|
7
|
+
*.egg-info
|
8
|
+
|
9
|
+
# Virtual environments
|
10
|
+
.venv
|
11
|
+
|
12
|
+
# Environment variables
|
13
|
+
.env
|
14
|
+
.env.local
|
15
|
+
|
16
|
+
|
17
|
+
dump/
|
18
|
+
|
19
|
+
|
20
|
+
# SQLite database
|
21
|
+
*.db
|
22
|
+
*.sqlite
|
23
|
+
*.sqlite3
|
24
|
+
*.db-shm
|
25
|
+
*.db-wal
|
26
|
+
|
27
|
+
|
28
|
+
# Pyright
|
29
|
+
.pyright
|
30
|
+
|
31
|
+
# Ruff
|
32
|
+
.ruff_cache
|
33
|
+
|
34
|
+
# Pytest
|
35
|
+
.pytest_cache
|
36
|
+
|
37
|
+
|
38
|
+
# Local config
|
39
|
+
local_config.json
|
40
|
+
|
41
|
+
|
42
|
+
# Test Artifacts
|
43
|
+
test_artifacts/
|
44
|
+
src/universal_mcp/applications/complex/
|
45
|
+
src/universal_mcp/applications/test/
|
46
|
+
src/universal_mcp/applications/test_with_docs/
|
47
|
+
src/universal_mcp/applications/test_without_docs/
|
@@ -0,0 +1,24 @@
|
|
1
|
+
fail_fast: true
|
2
|
+
|
3
|
+
repos:
|
4
|
+
- repo: https://github.com/pre-commit/mirrors-prettier
|
5
|
+
rev: v3.1.0
|
6
|
+
hooks:
|
7
|
+
- id: prettier
|
8
|
+
types_or: [yaml, json5]
|
9
|
+
|
10
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
11
|
+
rev: v0.8.1
|
12
|
+
hooks:
|
13
|
+
- id: ruff-format
|
14
|
+
- id: ruff
|
15
|
+
args: [--fix, --exit-non-zero-on-fix]
|
16
|
+
|
17
|
+
- repo: local
|
18
|
+
hooks:
|
19
|
+
- id: uv-lock-check
|
20
|
+
name: Check uv.lock is up to date
|
21
|
+
entry: uv lock --check
|
22
|
+
language: system
|
23
|
+
files: ^(pyproject\.toml|uv\.lock)$
|
24
|
+
pass_filenames: false
|
@@ -1,25 +1,38 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: universal-mcp
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.2rc1
|
4
4
|
Summary: Universal MCP acts as a middle ware for your API applications. It can store your credentials, authorize, enable disable apps on the fly and much more.
|
5
5
|
Author-email: Manoj Bajaj <manojbajaj95@gmail.com>
|
6
6
|
Requires-Python: >=3.11
|
7
|
+
Requires-Dist: keyring>=25.6.0
|
8
|
+
Requires-Dist: litellm>=1.30.7
|
7
9
|
Requires-Dist: loguru>=0.7.3
|
10
|
+
Requires-Dist: markitdown[all]>=0.1.1
|
8
11
|
Requires-Dist: mcp>=1.5.0
|
9
12
|
Requires-Dist: pydantic-settings>=2.8.1
|
10
13
|
Requires-Dist: pydantic>=2.11.1
|
11
14
|
Requires-Dist: pyyaml>=6.0.2
|
12
15
|
Requires-Dist: typer>=0.15.2
|
16
|
+
Provides-Extra: dev
|
17
|
+
Requires-Dist: litellm>=1.30.7; extra == 'dev'
|
18
|
+
Requires-Dist: pyright>=1.1.398; extra == 'dev'
|
19
|
+
Requires-Dist: pytest-asyncio>=0.26.0; extra == 'dev'
|
20
|
+
Requires-Dist: pytest>=8.3.5; extra == 'dev'
|
21
|
+
Requires-Dist: ruff>=0.11.4; extra == 'dev'
|
22
|
+
Provides-Extra: e2b
|
23
|
+
Requires-Dist: e2b-code-interpreter>=1.2.0; extra == 'e2b'
|
24
|
+
Provides-Extra: firecrawl
|
25
|
+
Requires-Dist: firecrawl-py>=1.15.0; extra == 'firecrawl'
|
13
26
|
Provides-Extra: playground
|
14
27
|
Requires-Dist: fastapi[standard]>=0.115.12; extra == 'playground'
|
15
28
|
Requires-Dist: langchain-anthropic>=0.3.10; extra == 'playground'
|
16
29
|
Requires-Dist: langchain-mcp-adapters>=0.0.3; extra == 'playground'
|
17
30
|
Requires-Dist: langgraph-checkpoint-sqlite>=2.0.6; extra == 'playground'
|
18
31
|
Requires-Dist: langgraph>=0.3.24; extra == 'playground'
|
32
|
+
Requires-Dist: python-dotenv>=1.0.1; extra == 'playground'
|
19
33
|
Requires-Dist: streamlit>=1.44.1; extra == 'playground'
|
20
|
-
Provides-Extra:
|
21
|
-
Requires-Dist:
|
22
|
-
Requires-Dist: pytest>=8.3.5; extra == 'test'
|
34
|
+
Provides-Extra: serpapi
|
35
|
+
Requires-Dist: google-search-results>=2.4.2; extra == 'serpapi'
|
23
36
|
Description-Content-Type: text/markdown
|
24
37
|
|
25
38
|
# Universal MCP
|
@@ -166,7 +179,11 @@ AgentR includes a command-line interface for common operations:
|
|
166
179
|
agentr version
|
167
180
|
|
168
181
|
# Generate API client from OpenAPI schema
|
169
|
-
|
182
|
+
|
183
|
+
# Use the name of the API as the output filename (e.g., twitter, petstore, github)
|
184
|
+
universal_mcp generate --schema petstore.json --output outputfilename
|
185
|
+
|
186
|
+
# The tool will create src/universal_mcp/applications/petstore/ with app.py and README.md
|
170
187
|
|
171
188
|
# Run the test server
|
172
189
|
agentr run
|
@@ -142,7 +142,11 @@ AgentR includes a command-line interface for common operations:
|
|
142
142
|
agentr version
|
143
143
|
|
144
144
|
# Generate API client from OpenAPI schema
|
145
|
-
|
145
|
+
|
146
|
+
# Use the name of the API as the output filename (e.g., twitter, petstore, github)
|
147
|
+
universal_mcp generate --schema petstore.json --output outputfilename
|
148
|
+
|
149
|
+
# The tool will create src/universal_mcp/applications/petstore/ with app.py and README.md
|
146
150
|
|
147
151
|
# Run the test server
|
148
152
|
agentr run
|
@@ -0,0 +1,149 @@
|
|
1
|
+
[project]
|
2
|
+
name = "universal-mcp"
|
3
|
+
version = "0.1.2rc1"
|
4
|
+
description = "Universal MCP acts as a middle ware for your API applications. It can store your credentials, authorize, enable disable apps on the fly and much more."
|
5
|
+
readme = "README.md"
|
6
|
+
authors = [
|
7
|
+
{ name = "Manoj Bajaj", email = "manojbajaj95@gmail.com" }
|
8
|
+
]
|
9
|
+
requires-python = ">=3.11"
|
10
|
+
dependencies = [
|
11
|
+
"pydantic>=2.11.1",
|
12
|
+
"pydantic-settings>=2.8.1",
|
13
|
+
"loguru>=0.7.3",
|
14
|
+
"mcp>=1.5.0",
|
15
|
+
"pyyaml>=6.0.2",
|
16
|
+
"typer>=0.15.2",
|
17
|
+
"markitdown[all]>=0.1.1",
|
18
|
+
"keyring>=25.6.0",
|
19
|
+
"litellm>=1.30.7",
|
20
|
+
]
|
21
|
+
|
22
|
+
[project.optional-dependencies]
|
23
|
+
playground = [
|
24
|
+
"fastapi[standard]>=0.115.12",
|
25
|
+
"langchain-anthropic>=0.3.10",
|
26
|
+
"langchain-mcp-adapters>=0.0.3",
|
27
|
+
"langgraph>=0.3.24",
|
28
|
+
"langgraph-checkpoint-sqlite>=2.0.6",
|
29
|
+
"streamlit>=1.44.1",
|
30
|
+
"python-dotenv>=1.0.1",
|
31
|
+
]
|
32
|
+
dev = [
|
33
|
+
"ruff>=0.11.4",
|
34
|
+
"pytest>=8.3.5",
|
35
|
+
"pytest-asyncio>=0.26.0",
|
36
|
+
"pyright>=1.1.398",
|
37
|
+
"litellm>=1.30.7",
|
38
|
+
]
|
39
|
+
e2b = [
|
40
|
+
"e2b-code-interpreter>=1.2.0",
|
41
|
+
]
|
42
|
+
firecrawl=[
|
43
|
+
"firecrawl-py>=1.15.0",
|
44
|
+
]
|
45
|
+
serpapi = [
|
46
|
+
"google-search-results>=2.4.2",
|
47
|
+
]
|
48
|
+
[project.scripts]
|
49
|
+
universal_mcp = "universal_mcp.cli:app"
|
50
|
+
|
51
|
+
[build-system]
|
52
|
+
requires = ["hatchling"]
|
53
|
+
build-backend = "hatchling.build"
|
54
|
+
|
55
|
+
[tool.ruff]
|
56
|
+
# Exclude a variety of commonly ignored directories.
|
57
|
+
exclude = [
|
58
|
+
".bzr",
|
59
|
+
".direnv",
|
60
|
+
".eggs",
|
61
|
+
".git",
|
62
|
+
".git-rewrite",
|
63
|
+
".hg",
|
64
|
+
".ipynb_checkpoints",
|
65
|
+
".mypy_cache",
|
66
|
+
".nox",
|
67
|
+
".pants.d",
|
68
|
+
".pyenv",
|
69
|
+
".pytest_cache",
|
70
|
+
".pytype",
|
71
|
+
".ruff_cache",
|
72
|
+
".svn",
|
73
|
+
".tox",
|
74
|
+
".venv",
|
75
|
+
".vscode",
|
76
|
+
"__pypackages__",
|
77
|
+
"_build",
|
78
|
+
"buck-out",
|
79
|
+
"build",
|
80
|
+
"dist",
|
81
|
+
"node_modules",
|
82
|
+
"site-packages",
|
83
|
+
"venv",
|
84
|
+
]
|
85
|
+
|
86
|
+
# Same as Black.
|
87
|
+
line-length = 88
|
88
|
+
indent-width = 4
|
89
|
+
|
90
|
+
# Assume Python 3.12
|
91
|
+
target-version = "py312"
|
92
|
+
|
93
|
+
[tool.ruff.lint]
|
94
|
+
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
95
|
+
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
|
96
|
+
# McCabe complexity (`C901`) by default.
|
97
|
+
select = [
|
98
|
+
# pycodestyle
|
99
|
+
"E",
|
100
|
+
# Pyflakes
|
101
|
+
"F",
|
102
|
+
# pyupgrade
|
103
|
+
"UP",
|
104
|
+
# flake8-bugbear
|
105
|
+
"B",
|
106
|
+
# flake8-simplify
|
107
|
+
"SIM",
|
108
|
+
# isort
|
109
|
+
"I",
|
110
|
+
]
|
111
|
+
ignore = ['E501', 'B008']
|
112
|
+
|
113
|
+
# Allow fix for all enabled rules (when `--fix`) is provided.
|
114
|
+
fixable = ["ALL"]
|
115
|
+
unfixable = []
|
116
|
+
|
117
|
+
# Allow unused variables when underscore-prefixed.
|
118
|
+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
119
|
+
|
120
|
+
[tool.ruff.format]
|
121
|
+
# Like Black, use double quotes for strings.
|
122
|
+
quote-style = "double"
|
123
|
+
|
124
|
+
# Like Black, indent with spaces, rather than tabs.
|
125
|
+
indent-style = "space"
|
126
|
+
|
127
|
+
# Like Black, respect magic trailing commas.
|
128
|
+
skip-magic-trailing-comma = false
|
129
|
+
|
130
|
+
# Like Black, automatically detect the appropriate line ending.
|
131
|
+
line-ending = "auto"
|
132
|
+
|
133
|
+
# Enable auto-formatting of code examples in docstrings. Markdown,
|
134
|
+
# reStructuredText code/literal blocks and doctests are all supported.
|
135
|
+
#
|
136
|
+
# This is currently disabled by default, but it is planned for this
|
137
|
+
# to be opt-out in the future.
|
138
|
+
docstring-code-format = false
|
139
|
+
|
140
|
+
# Set the line length limit used when formatting code snippets in
|
141
|
+
# docstrings.
|
142
|
+
#
|
143
|
+
# This only has an effect when the `docstring-code-format` setting is
|
144
|
+
# enabled.
|
145
|
+
docstring-code-line-length = "dynamic"
|
146
|
+
|
147
|
+
[tool.pytest.ini_options]
|
148
|
+
asyncio_mode = "strict"
|
149
|
+
asyncio_default_fixture_loop_scope = "function"
|
@@ -0,0 +1,65 @@
|
|
1
|
+
## Running the Playground
|
2
|
+
|
3
|
+
### Automated Startup (Recommended)
|
4
|
+
|
5
|
+
To easily start all necessary services (MCP Server, FastAPI Backend, Streamlit Frontend), you can use the provided Python startup script. This script will attempt to open each service in sequence and ensures they are terminated gracefully upon interruption.
|
6
|
+
|
7
|
+
**Prerequisites:**
|
8
|
+
|
9
|
+
* Python 3 installed and available in your PATH.
|
10
|
+
* All project dependencies installed (ensure you've run `pip install ...` for all requirements).
|
11
|
+
* **`local_config.json` file:** This configuration file for the MCP server must exist in the **project root directory**. It should contain a JSON array defining the MCP tools to load. For example:
|
12
|
+
```json
|
13
|
+
{
|
14
|
+
"name": "Local Server",
|
15
|
+
"description": "Local server for testing",
|
16
|
+
"type": "local",
|
17
|
+
"transport": "sse",
|
18
|
+
"apps": [
|
19
|
+
{
|
20
|
+
"name": "zenquotes"
|
21
|
+
}
|
22
|
+
]
|
23
|
+
}
|
24
|
+
```
|
25
|
+
*(Adapt the content based on the actual MCP tools you intend to use.)*
|
26
|
+
* A compatible terminal environment for the script:
|
27
|
+
* On **macOS**, requires the standard `Terminal.app`.
|
28
|
+
* On **Linux**, assumes `gnome-terminal` is available (common on Ubuntu/Fedora). You may need to modify `src/playground/__main__.py` if you use a different terminal like `konsole` or `xfce4-terminal`.
|
29
|
+
* On **Windows**, requires the standard `cmd.exe`.
|
30
|
+
|
31
|
+
**Instructions:**
|
32
|
+
|
33
|
+
1. Open your terminal application.
|
34
|
+
2. Navigate (`cd`) to the **root directory** of this project (the directory containing the `src/` folder and `local_config.json`).
|
35
|
+
3. Execute the startup script using Python:
|
36
|
+
|
37
|
+
```bash
|
38
|
+
python src/playground
|
39
|
+
```
|
40
|
+
|
41
|
+
*(Note: If your system uses `python3` to invoke Python 3, use that command instead: `python3 src/playground`)*
|
42
|
+
|
43
|
+
After running the command, you should see messages indicating that the components are being launched.
|
44
|
+
|
45
|
+
### Manual Startup (Alternative)
|
46
|
+
|
47
|
+
If you prefer, or if the automated script does not work on your specific system configuration, you can still start each component manually. Make sure you run each command from the **project root directory** in a separate terminal window:
|
48
|
+
|
49
|
+
1. **Terminal 1 (MCP Server):**
|
50
|
+
*(Ensure `local_config.json` exists in the project root as described above)*
|
51
|
+
```bash
|
52
|
+
universal_mcp run -c local_config.json
|
53
|
+
```
|
54
|
+
2. **Terminal 2 (FastAPI App):**
|
55
|
+
```bash
|
56
|
+
fastapi run src/playground
|
57
|
+
```
|
58
|
+
3. **Terminal 3 (Streamlit App):**
|
59
|
+
```bash
|
60
|
+
streamlit run src/playground/streamlit.py
|
61
|
+
```
|
62
|
+
|
63
|
+
### Stopping the Services
|
64
|
+
|
65
|
+
To stop the running services, go to each of the terminal windows that were opened (either by the script or manually) and press `Ctrl + C`.
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import subprocess
|
2
|
+
import time
|
3
|
+
|
4
|
+
from loguru import logger
|
5
|
+
|
6
|
+
|
7
|
+
def main():
|
8
|
+
processes = []
|
9
|
+
|
10
|
+
# Start MCP server first
|
11
|
+
mcp_process = subprocess.Popen(["universal_mcp", "run", "-c", "local_config.json"])
|
12
|
+
processes.append(mcp_process)
|
13
|
+
time.sleep(3) # Give MCP server time to start
|
14
|
+
logger.info("MCP server started")
|
15
|
+
|
16
|
+
# Start FastAPI app second
|
17
|
+
fastapi_process = subprocess.Popen(["fastapi", "run", "src/playground"])
|
18
|
+
processes.append(fastapi_process)
|
19
|
+
time.sleep(3) # Give FastAPI time to start
|
20
|
+
logger.info("FastAPI app started")
|
21
|
+
# Start Streamlit app last
|
22
|
+
streamlit_process = subprocess.Popen(
|
23
|
+
["streamlit", "run", "src/playground/streamlit.py"]
|
24
|
+
)
|
25
|
+
processes.append(streamlit_process)
|
26
|
+
logger.info("Streamlit app started")
|
27
|
+
try:
|
28
|
+
for p in processes:
|
29
|
+
p.wait()
|
30
|
+
except KeyboardInterrupt:
|
31
|
+
for p in processes:
|
32
|
+
p.terminate()
|
33
|
+
p.wait()
|
34
|
+
|
35
|
+
|
36
|
+
if __name__ == "__main__":
|
37
|
+
main()
|
@@ -1,9 +1,10 @@
|
|
1
|
+
import asyncio
|
1
2
|
from contextlib import asynccontextmanager
|
3
|
+
|
2
4
|
from langchain_anthropic import ChatAnthropic
|
3
|
-
from langgraph.prebuilt import create_react_agent
|
4
5
|
from langchain_core.messages import HumanMessage
|
5
|
-
import asyncio
|
6
6
|
from langchain_mcp_adapters.client import MultiServerMCPClient
|
7
|
+
from langgraph.prebuilt import create_react_agent
|
7
8
|
|
8
9
|
|
9
10
|
@asynccontextmanager
|
@@ -56,7 +56,7 @@ class AgentClient:
|
|
56
56
|
)
|
57
57
|
response.raise_for_status()
|
58
58
|
except httpx.HTTPError as e:
|
59
|
-
raise AgentClientError(f"Error getting service info: {e}")
|
59
|
+
raise AgentClientError(f"Error getting service info: {e}") from e
|
60
60
|
|
61
61
|
async def ainvoke(
|
62
62
|
self,
|
@@ -92,7 +92,7 @@ class AgentClient:
|
|
92
92
|
)
|
93
93
|
response.raise_for_status()
|
94
94
|
except httpx.HTTPError as e:
|
95
|
-
raise AgentClientError(f"Error: {e}")
|
95
|
+
raise AgentClientError(f"Error: {e}") from e
|
96
96
|
|
97
97
|
return ChatMessage.model_validate(response.json())
|
98
98
|
|
@@ -131,7 +131,7 @@ class AgentClient:
|
|
131
131
|
)
|
132
132
|
response.raise_for_status()
|
133
133
|
except httpx.HTTPError as e:
|
134
|
-
raise AgentClientError(f"Error: {e}")
|
134
|
+
raise AgentClientError(f"Error: {e}") from e
|
135
135
|
|
136
136
|
return ChatMessage.model_validate(response.json())
|
137
137
|
|
@@ -144,14 +144,14 @@ class AgentClient:
|
|
144
144
|
try:
|
145
145
|
parsed = json.loads(data)
|
146
146
|
except Exception as e:
|
147
|
-
raise Exception(f"Error JSON parsing message from server: {e}")
|
147
|
+
raise Exception(f"Error JSON parsing message from server: {e}") from e
|
148
148
|
match parsed["type"]:
|
149
149
|
case "message":
|
150
150
|
# Convert the JSON formatted message to an AnyMessage
|
151
151
|
try:
|
152
152
|
return ChatMessage.model_validate(parsed["content"])
|
153
153
|
except Exception as e:
|
154
|
-
raise Exception(f"Server returned invalid message: {e}")
|
154
|
+
raise Exception(f"Server returned invalid message: {e}") from e
|
155
155
|
case "token":
|
156
156
|
# Yield the str token directly
|
157
157
|
return parsed["content"]
|
@@ -208,7 +208,7 @@ class AgentClient:
|
|
208
208
|
break
|
209
209
|
yield parsed
|
210
210
|
except httpx.HTTPError as e:
|
211
|
-
raise AgentClientError(f"Error: {e}")
|
211
|
+
raise AgentClientError(f"Error: {e}") from e
|
212
212
|
|
213
213
|
async def astream(
|
214
214
|
self,
|
@@ -261,7 +261,7 @@ class AgentClient:
|
|
261
261
|
break
|
262
262
|
yield parsed
|
263
263
|
except httpx.HTTPError as e:
|
264
|
-
raise AgentClientError(f"Error: {e}")
|
264
|
+
raise AgentClientError(f"Error: {e}") from e
|
265
265
|
|
266
266
|
def get_history(
|
267
267
|
self,
|
@@ -283,6 +283,6 @@ class AgentClient:
|
|
283
283
|
)
|
284
284
|
response.raise_for_status()
|
285
285
|
except httpx.HTTPError as e:
|
286
|
-
raise AgentClientError(f"Error: {e}")
|
286
|
+
raise AgentClientError(f"Error: {e}") from e
|
287
287
|
|
288
288
|
return ChatHistory.model_validate(response.json())
|
@@ -11,6 +11,7 @@ from langchain_core._api import LangChainBetaWarning
|
|
11
11
|
from langchain_core.messages import AnyMessage, HumanMessage
|
12
12
|
from langchain_core.runnables import RunnableConfig
|
13
13
|
from langgraph.types import Command
|
14
|
+
from loguru import logger
|
14
15
|
|
15
16
|
from playground.agents.react import create_agent
|
16
17
|
from playground.memory import initialize_database
|
@@ -26,7 +27,6 @@ from playground.utils import (
|
|
26
27
|
langchain_to_chat_message,
|
27
28
|
remove_tool_calls,
|
28
29
|
)
|
29
|
-
from loguru import logger
|
30
30
|
|
31
31
|
warnings.filterwarnings("ignore", category=LangChainBetaWarning)
|
32
32
|
|
@@ -40,13 +40,12 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
|
40
40
|
Configurable lifespan that initializes the appropriate database checkpointer based on settings.
|
41
41
|
"""
|
42
42
|
try:
|
43
|
-
async with create_agent() as react_agent:
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
yield
|
43
|
+
async with create_agent() as react_agent, initialize_database() as saver:
|
44
|
+
await saver.setup()
|
45
|
+
global agent
|
46
|
+
agent = react_agent
|
47
|
+
agent.checkpointer = saver
|
48
|
+
yield
|
50
49
|
except Exception as e:
|
51
50
|
logger.error(f"Error during database initialization: {e}")
|
52
51
|
raise
|
@@ -102,7 +101,7 @@ async def invoke(user_input: UserInput) -> ChatMessage:
|
|
102
101
|
return output
|
103
102
|
except Exception as e:
|
104
103
|
logger.error(f"An exception occurred: {e}")
|
105
|
-
raise HTTPException(status_code=500, detail="Unexpected error")
|
104
|
+
raise HTTPException(status_code=500, detail="Unexpected error") from e
|
106
105
|
|
107
106
|
|
108
107
|
async def message_generator(user_input: StreamInput) -> AsyncGenerator[str, None]:
|
@@ -223,7 +222,7 @@ def history(input: ChatHistoryInput) -> ChatHistory:
|
|
223
222
|
return ChatHistory(messages=chat_messages)
|
224
223
|
except Exception as e:
|
225
224
|
logger.error(f"An exception occurred: {e}")
|
226
|
-
raise HTTPException(status_code=500, detail="Unexpected error")
|
225
|
+
raise HTTPException(status_code=500, detail="Unexpected error") from e
|
227
226
|
|
228
227
|
|
229
228
|
@app.get("/health")
|