functualize-mcp 0.1.0__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.
- functualize_mcp-0.1.0/.gitignore +101 -0
- functualize_mcp-0.1.0/PKG-INFO +86 -0
- functualize_mcp-0.1.0/README.md +65 -0
- functualize_mcp-0.1.0/examples/README.md +52 -0
- functualize_mcp-0.1.0/examples/weather_tools/jobs.py +62 -0
- functualize_mcp-0.1.0/examples/weather_tools/test_weather_tools.py +31 -0
- functualize_mcp-0.1.0/pyproject.toml +42 -0
- functualize_mcp-0.1.0/src/functualize_mcp/__init__.py +86 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_config.py +39 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_gate_strategy.py +208 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_history_tools.py +272 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_management_tools.py +337 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_plugin.py +401 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_schema_export.py +286 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_server.py +282 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_server_manager.py +358 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_state.py +45 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_task_tools.py +361 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_tools.py +529 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_translator.py +409 -0
- functualize_mcp-0.1.0/src/functualize_mcp/_workflow_tools.py +718 -0
- functualize_mcp-0.1.0/src/functualize_mcp/py.typed +0 -0
- functualize_mcp-0.1.0/tests/__init__.py +0 -0
- functualize_mcp-0.1.0/tests/conftest.py +122 -0
- functualize_mcp-0.1.0/tests/test_plugin.py +99 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
*.egg-info/
|
|
7
|
+
*.egg
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
*.whl
|
|
11
|
+
|
|
12
|
+
# Agents
|
|
13
|
+
.spec/archive/
|
|
14
|
+
.spec/features/
|
|
15
|
+
.spec/scrutiny-reports/
|
|
16
|
+
.spec/.agentic-coding
|
|
17
|
+
.spec/STATE.md
|
|
18
|
+
.spec/PROJECT.md
|
|
19
|
+
.spec/REQUIREMENTS.md
|
|
20
|
+
.spec/ROADMAP.md
|
|
21
|
+
.opencode/
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Virtual environments
|
|
25
|
+
.venv/
|
|
26
|
+
venv/
|
|
27
|
+
ENV/
|
|
28
|
+
|
|
29
|
+
# Testing
|
|
30
|
+
.coverage
|
|
31
|
+
.pytest_cache/
|
|
32
|
+
htmlcov/
|
|
33
|
+
.hypothesis/
|
|
34
|
+
snapshot_report.html
|
|
35
|
+
_*_result*.txt
|
|
36
|
+
_debug.txt
|
|
37
|
+
_tui_debug.txt
|
|
38
|
+
_tui_eval_debug.txt
|
|
39
|
+
|
|
40
|
+
# IDE
|
|
41
|
+
.idea/
|
|
42
|
+
*.swp
|
|
43
|
+
*.swo
|
|
44
|
+
*~
|
|
45
|
+
*.code-workspace
|
|
46
|
+
|
|
47
|
+
# Coding-agent tooling state (guards — these dirs are not part of the repo)
|
|
48
|
+
.kiro/
|
|
49
|
+
.moai/
|
|
50
|
+
|
|
51
|
+
# OS
|
|
52
|
+
.DS_Store
|
|
53
|
+
Thumbs.db
|
|
54
|
+
|
|
55
|
+
# Environment / secrets
|
|
56
|
+
.env
|
|
57
|
+
.env.*
|
|
58
|
+
!.env.example
|
|
59
|
+
|
|
60
|
+
# Agent scratch space (test output, temp scripts)
|
|
61
|
+
tmp/
|
|
62
|
+
|
|
63
|
+
# Local-only files (not for the repo)
|
|
64
|
+
*.local.md
|
|
65
|
+
*.local.*
|
|
66
|
+
|
|
67
|
+
# Personal notes
|
|
68
|
+
HUMAN_NOTE.md
|
|
69
|
+
|
|
70
|
+
# Distribution
|
|
71
|
+
dist/
|
|
72
|
+
|
|
73
|
+
# Documentation site build output
|
|
74
|
+
site/
|
|
75
|
+
|
|
76
|
+
# uv
|
|
77
|
+
.python-version
|
|
78
|
+
.functualize/cache.json
|
|
79
|
+
.functualize_cache.json
|
|
80
|
+
.todos/
|
|
81
|
+
.sidecar/
|
|
82
|
+
.sidecar-agent
|
|
83
|
+
.sidecar-task
|
|
84
|
+
.sidecar-pr
|
|
85
|
+
.sidecar-start.sh
|
|
86
|
+
.sidecar-base
|
|
87
|
+
.td-root
|
|
88
|
+
.functualize/
|
|
89
|
+
.import_linter_cache/
|
|
90
|
+
.mypy_cache/
|
|
91
|
+
.pytest_cache/
|
|
92
|
+
.ruff_cache/
|
|
93
|
+
|
|
94
|
+
# OmO / OpenCode agent run-continuation scratch state
|
|
95
|
+
.omo/
|
|
96
|
+
.mcp.json
|
|
97
|
+
.agentsroom/handoff-transcript-*.txt
|
|
98
|
+
.agentsroom/handoff-summary-*.md
|
|
99
|
+
|
|
100
|
+
# Internal pre-release audit reports (contain session IDs / local infra notes)
|
|
101
|
+
.release/
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: functualize-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP delivery adapter plugin for functualize — exposes jobs as MCP tools via FastMCP
|
|
5
|
+
Author-email: Mohammad Hakim Adiprasetya <viltohmyst@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Typing :: Typed
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Requires-Dist: fastmcp>=0.1.0
|
|
15
|
+
Requires-Dist: functualize<1.0.0,>=0.1.0
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: hypothesis>=6.82.0; extra == 'dev'
|
|
18
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# functualize-mcp
|
|
23
|
+
|
|
24
|
+
> **Status: Published** — Independently installable from PyPI.
|
|
25
|
+
|
|
26
|
+
MCP delivery adapter plugin for functualize — exposes jobs as MCP tools via FastMCP. External AI agents (Goose, Claude, Cursor) can discover and invoke functualize jobs through the standardized [Model Context Protocol](https://modelcontextprotocol.io/) interface without any manual wiring.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install functualize-mcp
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from functualize_mcp import JobToolTranslator, MCPToolDef, SchemaExporter
|
|
38
|
+
|
|
39
|
+
# Translate a job descriptor into an MCP tool definition
|
|
40
|
+
translator = JobToolTranslator()
|
|
41
|
+
tool_def = translator.translate(my_job_descriptor)
|
|
42
|
+
print(tool_def.name, tool_def.description)
|
|
43
|
+
|
|
44
|
+
# Export all job schemas as OpenAI function calling JSON
|
|
45
|
+
exporter = SchemaExporter(translator)
|
|
46
|
+
openai_json = exporter.export_openai(descriptors)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- Automatic job-to-tool translation with JSON Schema input generation from config models
|
|
52
|
+
- Dual transport support: stdio (default) and HTTP+SSE for networked agents
|
|
53
|
+
- Job visibility filtering via include/exclude tags and job name exclusion lists
|
|
54
|
+
- Multi-format schema export: JSON (MCP), Markdown, OpenAI function calling, TypeScript
|
|
55
|
+
- Multi-server management: start, list, and stop background HTTP servers
|
|
56
|
+
- Workflow inspection and advancement tools for paused workflow steps
|
|
57
|
+
- Async job execution with polling via `run_job_async` / `get_execution_status`
|
|
58
|
+
- AI outbound gate strategy for workflow checkpoint serialization
|
|
59
|
+
|
|
60
|
+
## API Reference
|
|
61
|
+
|
|
62
|
+
Public classes and functions exported by this plugin:
|
|
63
|
+
|
|
64
|
+
- `MCPAdapterPlugin` — Plugin entry point implementing the AdapterPlugin protocol; registers MCP tools at boot
|
|
65
|
+
- `MCPConfig` — Pydantic configuration model controlling transport, port, host, tag filters, and management features
|
|
66
|
+
- `MCPServer` — FastMCP wrapper that discovers jobs, registers tools, and serves via stdio or HTTP+SSE
|
|
67
|
+
- `JobToolTranslator` — Translates JobDescriptors into MCPToolDef instances using docstrings and config schemas
|
|
68
|
+
- `MCPToolDef` — Frozen dataclass representing an MCP tool (name, description, input_schema, annotations)
|
|
69
|
+
- `SchemaExporter` — Exports job schemas in JSON, Markdown, OpenAI, and TypeScript formats
|
|
70
|
+
- `MCPToolRegistry` — Registers core MCP tools (discover_jobs, get_job_schema, run_job, run_job_async, get_execution_status)
|
|
71
|
+
- `MCPHistoryToolRegistry` — Registers execution history inspection tools
|
|
72
|
+
- `MCPManagementToolRegistry` — Registers multi-server management meta-tools
|
|
73
|
+
- `MCPTaskToolRegistry` — Registers task-domain MCP tools
|
|
74
|
+
- `WorkflowToolProvider` — Registers workflow state inspection and resume tools
|
|
75
|
+
- `ServerManager` — Manages background MCP HTTP server processes (start, list, stop)
|
|
76
|
+
- `ServerInfo` — Dataclass describing a managed server (name, directory, port, pid, status)
|
|
77
|
+
- `AIOutboundGateResolver` — Gate strategy resolver for AI outbound workflow checkpoints
|
|
78
|
+
- `register_ai_outbound_gate_strategy()` — Registers the ai_outbound gate strategy and preset with an app
|
|
79
|
+
|
|
80
|
+
## Development
|
|
81
|
+
|
|
82
|
+
Run plugin tests:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv run pytest plugins/functualize-mcp/tests/ -v
|
|
86
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# functualize-mcp
|
|
2
|
+
|
|
3
|
+
> **Status: Published** — Independently installable from PyPI.
|
|
4
|
+
|
|
5
|
+
MCP delivery adapter plugin for functualize — exposes jobs as MCP tools via FastMCP. External AI agents (Goose, Claude, Cursor) can discover and invoke functualize jobs through the standardized [Model Context Protocol](https://modelcontextprotocol.io/) interface without any manual wiring.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install functualize-mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from functualize_mcp import JobToolTranslator, MCPToolDef, SchemaExporter
|
|
17
|
+
|
|
18
|
+
# Translate a job descriptor into an MCP tool definition
|
|
19
|
+
translator = JobToolTranslator()
|
|
20
|
+
tool_def = translator.translate(my_job_descriptor)
|
|
21
|
+
print(tool_def.name, tool_def.description)
|
|
22
|
+
|
|
23
|
+
# Export all job schemas as OpenAI function calling JSON
|
|
24
|
+
exporter = SchemaExporter(translator)
|
|
25
|
+
openai_json = exporter.export_openai(descriptors)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Features
|
|
29
|
+
|
|
30
|
+
- Automatic job-to-tool translation with JSON Schema input generation from config models
|
|
31
|
+
- Dual transport support: stdio (default) and HTTP+SSE for networked agents
|
|
32
|
+
- Job visibility filtering via include/exclude tags and job name exclusion lists
|
|
33
|
+
- Multi-format schema export: JSON (MCP), Markdown, OpenAI function calling, TypeScript
|
|
34
|
+
- Multi-server management: start, list, and stop background HTTP servers
|
|
35
|
+
- Workflow inspection and advancement tools for paused workflow steps
|
|
36
|
+
- Async job execution with polling via `run_job_async` / `get_execution_status`
|
|
37
|
+
- AI outbound gate strategy for workflow checkpoint serialization
|
|
38
|
+
|
|
39
|
+
## API Reference
|
|
40
|
+
|
|
41
|
+
Public classes and functions exported by this plugin:
|
|
42
|
+
|
|
43
|
+
- `MCPAdapterPlugin` — Plugin entry point implementing the AdapterPlugin protocol; registers MCP tools at boot
|
|
44
|
+
- `MCPConfig` — Pydantic configuration model controlling transport, port, host, tag filters, and management features
|
|
45
|
+
- `MCPServer` — FastMCP wrapper that discovers jobs, registers tools, and serves via stdio or HTTP+SSE
|
|
46
|
+
- `JobToolTranslator` — Translates JobDescriptors into MCPToolDef instances using docstrings and config schemas
|
|
47
|
+
- `MCPToolDef` — Frozen dataclass representing an MCP tool (name, description, input_schema, annotations)
|
|
48
|
+
- `SchemaExporter` — Exports job schemas in JSON, Markdown, OpenAI, and TypeScript formats
|
|
49
|
+
- `MCPToolRegistry` — Registers core MCP tools (discover_jobs, get_job_schema, run_job, run_job_async, get_execution_status)
|
|
50
|
+
- `MCPHistoryToolRegistry` — Registers execution history inspection tools
|
|
51
|
+
- `MCPManagementToolRegistry` — Registers multi-server management meta-tools
|
|
52
|
+
- `MCPTaskToolRegistry` — Registers task-domain MCP tools
|
|
53
|
+
- `WorkflowToolProvider` — Registers workflow state inspection and resume tools
|
|
54
|
+
- `ServerManager` — Manages background MCP HTTP server processes (start, list, stop)
|
|
55
|
+
- `ServerInfo` — Dataclass describing a managed server (name, directory, port, pid, status)
|
|
56
|
+
- `AIOutboundGateResolver` — Gate strategy resolver for AI outbound workflow checkpoints
|
|
57
|
+
- `register_ai_outbound_gate_strategy()` — Registers the ai_outbound gate strategy and preset with an app
|
|
58
|
+
|
|
59
|
+
## Development
|
|
60
|
+
|
|
61
|
+
Run plugin tests:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
uv run pytest plugins/functualize-mcp/tests/ -v
|
|
65
|
+
```
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# functualize-mcp Examples
|
|
2
|
+
|
|
3
|
+
The full MCP reference example: exposing jobs as MCP tools that AI agents (Claude, Cursor, Goose) can discover and call. For the minimal narrative version, see [`examples/quickstart/step6_mcp/`](../../../examples/quickstart/step6_mcp/).
|
|
4
|
+
|
|
5
|
+
| Directory | Demonstrates |
|
|
6
|
+
|-----------|--------------|
|
|
7
|
+
| [`weather_tools/`](weather_tools/) | Visibility control, rich `@job_metadata`, and the served tool surface |
|
|
8
|
+
|
|
9
|
+
## Serving
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cd plugins/functualize-mcp/examples/weather_tools
|
|
13
|
+
func mcp serve # stdio transport (default)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Wire into Claude Code:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"mcpServers": {
|
|
21
|
+
"weather": {
|
|
22
|
+
"command": "func",
|
|
23
|
+
"args": ["mcp", "serve"],
|
|
24
|
+
"cwd": "/path/to/weather_tools"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## The tool surface agents see
|
|
31
|
+
|
|
32
|
+
Beyond one tool per `visibility="external"` job, the server exposes framework tools:
|
|
33
|
+
|
|
34
|
+
| Tool | Purpose |
|
|
35
|
+
|------|---------|
|
|
36
|
+
| `discover_jobs` | List external jobs with descriptions and tags |
|
|
37
|
+
| `get_job_schema` | Full input schema (from the job's Pydantic config) |
|
|
38
|
+
| `run_job` / `run_job_async` | Execute synchronously or in the background |
|
|
39
|
+
| `get_execution_status` | Poll an async execution |
|
|
40
|
+
| `get_workflow_state` / `resume_workflow` | Drive gated workflows across turns |
|
|
41
|
+
|
|
42
|
+
Inspect what would be served without starting the server:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
func mcp tools
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Tests
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv run pytest plugins/functualize-mcp/examples/ -v
|
|
52
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Jobs exposed as MCP tools — visibility and metadata reference.
|
|
2
|
+
|
|
3
|
+
Serve with:
|
|
4
|
+
func mcp serve
|
|
5
|
+
|
|
6
|
+
`forecast` and `travel_advice` become MCP tools; `purge_cache` stays
|
|
7
|
+
hidden (visibility="internal"); `refresh` has no metadata and defaults
|
|
8
|
+
to hidden as well.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, Field
|
|
12
|
+
|
|
13
|
+
from functualize.job import RunContext
|
|
14
|
+
from functualize.job.decorators import job
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ForecastConfig(BaseModel):
|
|
18
|
+
"""Configuration for weather jobs — becomes the MCP tool's input schema."""
|
|
19
|
+
|
|
20
|
+
city: str = Field(description="City to check")
|
|
21
|
+
days: int = Field(default=3, ge=1, le=7, description="Days to forecast")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@job(
|
|
25
|
+
extra_description="Get a weather forecast for a city",
|
|
26
|
+
category="weather",
|
|
27
|
+
examples=["forecast --city Tokyo --days 5"],
|
|
28
|
+
tags=["weather", "safe", "read-only"],
|
|
29
|
+
visibility="external",
|
|
30
|
+
)
|
|
31
|
+
def forecast(config: ForecastConfig, rc: RunContext) -> str:
|
|
32
|
+
"""Fetch the weather forecast for the configured city."""
|
|
33
|
+
rc.log(f"Fetching {config.days}-day forecast for {config.city}...")
|
|
34
|
+
return f"{config.city}: 24°C, sunny for the next {config.days} days"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@job(
|
|
38
|
+
extra_description="Advise whether the weather suits outdoor travel plans",
|
|
39
|
+
category="weather",
|
|
40
|
+
tags=["weather", "advice"],
|
|
41
|
+
visibility="external",
|
|
42
|
+
)
|
|
43
|
+
def travel_advice(config: ForecastConfig, rc: RunContext) -> str:
|
|
44
|
+
"""Turn the forecast into a go/no-go travel recommendation."""
|
|
45
|
+
rc.log(f"Evaluating travel conditions for {config.city}...")
|
|
46
|
+
return f"{config.city} looks great for outdoor plans this week"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@job(
|
|
50
|
+
extra_description="Purge the local forecast cache",
|
|
51
|
+
visibility="internal",
|
|
52
|
+
)
|
|
53
|
+
def purge_cache(rc: RunContext) -> str:
|
|
54
|
+
"""Internal maintenance — hidden from MCP agents."""
|
|
55
|
+
rc.log("Purging forecast cache...")
|
|
56
|
+
return "cache purged"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def refresh(rc: RunContext) -> str:
|
|
60
|
+
"""No metadata at all — also not exposed as an MCP tool."""
|
|
61
|
+
rc.log("Refreshing data sources...")
|
|
62
|
+
return "refreshed"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Tests for the weather_tools MCP example: the visibility contract."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from unittest.mock import MagicMock
|
|
6
|
+
|
|
7
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
8
|
+
|
|
9
|
+
from jobs import ForecastConfig, forecast, purge_cache, refresh, travel_advice
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_external_jobs_carry_full_metadata():
|
|
13
|
+
for job in (forecast, travel_advice):
|
|
14
|
+
decl = job.__functualize_job__
|
|
15
|
+
assert decl.visibility == "external"
|
|
16
|
+
assert decl.extra_description
|
|
17
|
+
assert "weather" in decl.tags
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_internal_job_is_marked_hidden():
|
|
21
|
+
assert purge_cache.__functualize_job__.visibility == "internal"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_unannotated_job_has_no_declaration():
|
|
25
|
+
assert not hasattr(refresh, "__functualize_job__")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_jobs_run_directly():
|
|
29
|
+
rc = MagicMock()
|
|
30
|
+
assert "Tokyo" in forecast(ForecastConfig(city="Tokyo"), rc)
|
|
31
|
+
assert "Tokyo" in travel_advice(ForecastConfig(city="Tokyo"), rc)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "functualize-mcp"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "MCP delivery adapter plugin for functualize — exposes jobs as MCP tools via FastMCP"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Mohammad Hakim Adiprasetya", email = "viltohmyst@gmail.com" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.11",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Programming Language :: Python :: 3.13",
|
|
17
|
+
"Typing :: Typed",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"functualize>=0.1.0,<1.0.0",
|
|
21
|
+
"fastmcp>=0.1.0",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.entry-points."functualize.plugins"]
|
|
25
|
+
mcp = "functualize_mcp:MCPAdapterPlugin"
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
dev = [
|
|
29
|
+
"pytest>=7.4.0",
|
|
30
|
+
"pytest-cov>=4.1.0",
|
|
31
|
+
"hypothesis>=6.82.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["hatchling"]
|
|
36
|
+
build-backend = "hatchling.build"
|
|
37
|
+
|
|
38
|
+
[tool.uv.sources]
|
|
39
|
+
functualize = { workspace = true }
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.wheel]
|
|
42
|
+
packages = ["src/functualize_mcp"]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Functualize MCP Plugin — exposes jobs as MCP tools via FastMCP.
|
|
2
|
+
|
|
3
|
+
Provides an MCP delivery adapter that translates functualize jobs into
|
|
4
|
+
MCP tools, allowing external AI agents (Goose, Claude, Cursor) to discover
|
|
5
|
+
and invoke jobs through the Model Context Protocol.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from functualize_mcp._plugin import MCPAdapterPlugin
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"AI_OUTBOUND_PRESET_NAME",
|
|
12
|
+
"AI_OUTBOUND_PRESET_STRATEGIES",
|
|
13
|
+
"AI_OUTBOUND_STRATEGY_NAME",
|
|
14
|
+
"AIOutboundGateResolver",
|
|
15
|
+
"MCPAdapterPlugin",
|
|
16
|
+
"MCPConfig",
|
|
17
|
+
"MCPHistoryToolRegistry",
|
|
18
|
+
"MCPManagementToolRegistry",
|
|
19
|
+
"MCPServer",
|
|
20
|
+
"MCPTaskToolRegistry",
|
|
21
|
+
"MCPToolRegistry",
|
|
22
|
+
"JobToolTranslator",
|
|
23
|
+
"MCPToolDef",
|
|
24
|
+
"SchemaExporter",
|
|
25
|
+
"ServerInfo",
|
|
26
|
+
"ServerManager",
|
|
27
|
+
"WorkflowToolProvider",
|
|
28
|
+
"register_ai_outbound_gate_strategy",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Lazy loading for heavy symbols (PEP 562)
|
|
33
|
+
_LAZY_IMPORTS: dict[str, tuple[str, str]] = {
|
|
34
|
+
"MCPConfig": ("functualize_mcp._config", "MCPConfig"),
|
|
35
|
+
"AI_OUTBOUND_PRESET_NAME": (
|
|
36
|
+
"functualize_mcp._gate_strategy",
|
|
37
|
+
"AI_OUTBOUND_PRESET_NAME",
|
|
38
|
+
),
|
|
39
|
+
"AI_OUTBOUND_PRESET_STRATEGIES": (
|
|
40
|
+
"functualize_mcp._gate_strategy",
|
|
41
|
+
"AI_OUTBOUND_PRESET_STRATEGIES",
|
|
42
|
+
),
|
|
43
|
+
"AI_OUTBOUND_STRATEGY_NAME": (
|
|
44
|
+
"functualize_mcp._gate_strategy",
|
|
45
|
+
"AI_OUTBOUND_STRATEGY_NAME",
|
|
46
|
+
),
|
|
47
|
+
"AIOutboundGateResolver": (
|
|
48
|
+
"functualize_mcp._gate_strategy",
|
|
49
|
+
"AIOutboundGateResolver",
|
|
50
|
+
),
|
|
51
|
+
"register_ai_outbound_gate_strategy": (
|
|
52
|
+
"functualize_mcp._gate_strategy",
|
|
53
|
+
"register_ai_outbound_gate_strategy",
|
|
54
|
+
),
|
|
55
|
+
"MCPHistoryToolRegistry": (
|
|
56
|
+
"functualize_mcp._history_tools",
|
|
57
|
+
"MCPHistoryToolRegistry",
|
|
58
|
+
),
|
|
59
|
+
"MCPManagementToolRegistry": (
|
|
60
|
+
"functualize_mcp._management_tools",
|
|
61
|
+
"MCPManagementToolRegistry",
|
|
62
|
+
),
|
|
63
|
+
"SchemaExporter": ("functualize_mcp._schema_export", "SchemaExporter"),
|
|
64
|
+
"MCPServer": ("functualize_mcp._server", "MCPServer"),
|
|
65
|
+
"ServerInfo": ("functualize_mcp._server_manager", "ServerInfo"),
|
|
66
|
+
"ServerManager": ("functualize_mcp._server_manager", "ServerManager"),
|
|
67
|
+
"MCPTaskToolRegistry": ("functualize_mcp._task_tools", "MCPTaskToolRegistry"),
|
|
68
|
+
"MCPToolRegistry": ("functualize_mcp._tools", "MCPToolRegistry"),
|
|
69
|
+
"JobToolTranslator": ("functualize_mcp._translator", "JobToolTranslator"),
|
|
70
|
+
"MCPToolDef": ("functualize_mcp._translator", "MCPToolDef"),
|
|
71
|
+
"WorkflowToolProvider": ("functualize_mcp._workflow_tools", "WorkflowToolProvider"),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def __getattr__(name: str):
|
|
76
|
+
"""Lazy-load heavy symbols on first access (PEP 562)."""
|
|
77
|
+
if name in _LAZY_IMPORTS:
|
|
78
|
+
module_path, attr_name = _LAZY_IMPORTS[name]
|
|
79
|
+
import importlib
|
|
80
|
+
|
|
81
|
+
module = importlib.import_module(module_path)
|
|
82
|
+
value = getattr(module, attr_name)
|
|
83
|
+
# Cache it on the module for subsequent fast access
|
|
84
|
+
globals()[name] = value
|
|
85
|
+
return value
|
|
86
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""MCP adapter configuration model.
|
|
2
|
+
|
|
3
|
+
Defines MCPConfig pydantic model with transport, networking, filtering,
|
|
4
|
+
and management settings for the MCP server.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Literal
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, Field
|
|
12
|
+
|
|
13
|
+
__all__ = ["MCPConfig"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MCPConfig(BaseModel):
|
|
17
|
+
"""Configuration for the MCP delivery adapter.
|
|
18
|
+
|
|
19
|
+
Read from the ``[mcp]`` config section. Controls transport mode,
|
|
20
|
+
network binding, job visibility filtering, and management features.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
transport: Server transport mode — "stdio" (default) or "http".
|
|
24
|
+
port: HTTP port when using HTTP+SSE transport.
|
|
25
|
+
host: Bind address for HTTP transport.
|
|
26
|
+
include_tags: Only expose jobs tagged with at least one of these tags.
|
|
27
|
+
Empty list means no tag filtering (expose all visible jobs).
|
|
28
|
+
exclude_tags: Hide jobs tagged with any of these tags.
|
|
29
|
+
exclude_jobs: Hide specific jobs by name from MCP tool listings.
|
|
30
|
+
enable_management: When True, expose multi-server management meta-tools.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
transport: Literal["stdio", "http"] = "stdio"
|
|
34
|
+
port: int = Field(default=8080, ge=1024, le=65535)
|
|
35
|
+
host: str = "127.0.0.1"
|
|
36
|
+
include_tags: list[str] = Field(default_factory=list)
|
|
37
|
+
exclude_tags: list[str] = Field(default_factory=list)
|
|
38
|
+
exclude_jobs: list[str] = Field(default_factory=list)
|
|
39
|
+
enable_management: bool = False
|