moss-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.
- moss_mcp-0.1.0/LICENSE +21 -0
- moss_mcp-0.1.0/PKG-INFO +137 -0
- moss_mcp-0.1.0/README.md +108 -0
- moss_mcp-0.1.0/moss_mcp/__init__.py +59 -0
- moss_mcp-0.1.0/moss_mcp/client.py +216 -0
- moss_mcp-0.1.0/moss_mcp/middleware.py +264 -0
- moss_mcp-0.1.0/moss_mcp/server.py +259 -0
- moss_mcp-0.1.0/moss_mcp/signing.py +459 -0
- moss_mcp-0.1.0/pyproject.toml +54 -0
moss_mcp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MOSS Computing
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
moss_mcp-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: moss-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MOSS integration for Model Context Protocol (MCP) - Protocol-level governance for AI agent tools
|
|
5
|
+
Project-URL: Homepage, https://mosscomputing.com
|
|
6
|
+
Project-URL: Documentation, https://docs.mosscomputing.com/sdks/mcp
|
|
7
|
+
Project-URL: Repository, https://github.com/mosscomputing/moss-mcp
|
|
8
|
+
Project-URL: Issues, https://github.com/mosscomputing/moss-mcp/issues
|
|
9
|
+
Author-email: MOSS Computing <support@mosscomputing.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agent-security,ai-governance,compliance,cryptographic-signing,mcp,ml-dsa-44,model-context-protocol,moss
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Security :: Cryptography
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Requires-Dist: httpx>=0.24.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# moss-mcp
|
|
31
|
+
|
|
32
|
+
Protocol-level governance for Model Context Protocol (MCP) using ML-DSA-44 post-quantum cryptography.
|
|
33
|
+
|
|
34
|
+
## Overview
|
|
35
|
+
|
|
36
|
+
MOSS MCP integration signs ALL MCP tool calls with NIST FIPS 204 post-quantum cryptography. By intercepting at the MCP transport layer, MOSS governs every tool invocation regardless of the agent framework.
|
|
37
|
+
|
|
38
|
+
| Approach | Coverage | Bypassable? |
|
|
39
|
+
|----------|----------|-------------|
|
|
40
|
+
| Framework SDKs | Per-framework | Yes |
|
|
41
|
+
| **MCP Integration** | **All tools** | **No** |
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install moss-mcp
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from mcp import Client
|
|
53
|
+
from moss_mcp import wrap_mcp_client
|
|
54
|
+
|
|
55
|
+
# Wrap MCP client - all tool calls are now signed
|
|
56
|
+
moss_client = wrap_mcp_client(client, agent_id="my-agent")
|
|
57
|
+
|
|
58
|
+
# Use normally - signing happens automatically
|
|
59
|
+
result = await moss_client.call_tool("send_email", {
|
|
60
|
+
"to": "user@example.com",
|
|
61
|
+
"body": "Hello"
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Integration Options
|
|
66
|
+
|
|
67
|
+
### Option 1: Client Wrapper
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from moss_mcp import wrap_mcp_client
|
|
71
|
+
moss_client = wrap_mcp_client(client, agent_id="my-agent")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Option 2: MCP Server
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from moss_mcp import MOSSMCPServer
|
|
78
|
+
|
|
79
|
+
server = MOSSMCPServer(agent_id="email-service")
|
|
80
|
+
|
|
81
|
+
@server.tool()
|
|
82
|
+
async def send_email(to: str, body: str) -> str:
|
|
83
|
+
return f"Sent to {to}"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Option 3: Middleware
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from moss_mcp import MOSSMCPMiddleware
|
|
90
|
+
|
|
91
|
+
middleware = MOSSMCPMiddleware(
|
|
92
|
+
agent_id="my-service",
|
|
93
|
+
block_on_policy_violation=True,
|
|
94
|
+
)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Configuration
|
|
98
|
+
|
|
99
|
+
| Variable | Description | Default |
|
|
100
|
+
|----------|-------------|---------|
|
|
101
|
+
| `MOSS_API_KEY` | Enterprise API key | None (local mode) |
|
|
102
|
+
| `MOSS_API_URL` | API endpoint | `https://api.mosscomputing.com` |
|
|
103
|
+
|
|
104
|
+
## Policy Enforcement
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from moss_mcp.server import PolicyViolationError
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
await moss_client.call_tool("execute_trade", {"amount": 1000000})
|
|
111
|
+
except PolicyViolationError as e:
|
|
112
|
+
print(f"Blocked: {e.reason}")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Causal Chaining
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
result1 = await sign_tool_request_async("analyze_data", {...}, agent_id="agent-1")
|
|
119
|
+
|
|
120
|
+
result2 = await sign_tool_request_async(
|
|
121
|
+
"make_decision",
|
|
122
|
+
{...},
|
|
123
|
+
agent_id="agent-2",
|
|
124
|
+
parent_sig=result1.signature_id
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Links
|
|
129
|
+
|
|
130
|
+
- [Documentation](https://docs.mosscomputing.com/sdks/mcp)
|
|
131
|
+
- [API Reference](https://api.mosscomputing.com/docs)
|
|
132
|
+
- [Dashboard](https://app.mosscomputing.com)
|
|
133
|
+
- [MCP Specification](https://modelcontextprotocol.io)
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
moss_mcp-0.1.0/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# moss-mcp
|
|
2
|
+
|
|
3
|
+
Protocol-level governance for Model Context Protocol (MCP) using ML-DSA-44 post-quantum cryptography.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
MOSS MCP integration signs ALL MCP tool calls with NIST FIPS 204 post-quantum cryptography. By intercepting at the MCP transport layer, MOSS governs every tool invocation regardless of the agent framework.
|
|
8
|
+
|
|
9
|
+
| Approach | Coverage | Bypassable? |
|
|
10
|
+
|----------|----------|-------------|
|
|
11
|
+
| Framework SDKs | Per-framework | Yes |
|
|
12
|
+
| **MCP Integration** | **All tools** | **No** |
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install moss-mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from mcp import Client
|
|
24
|
+
from moss_mcp import wrap_mcp_client
|
|
25
|
+
|
|
26
|
+
# Wrap MCP client - all tool calls are now signed
|
|
27
|
+
moss_client = wrap_mcp_client(client, agent_id="my-agent")
|
|
28
|
+
|
|
29
|
+
# Use normally - signing happens automatically
|
|
30
|
+
result = await moss_client.call_tool("send_email", {
|
|
31
|
+
"to": "user@example.com",
|
|
32
|
+
"body": "Hello"
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Integration Options
|
|
37
|
+
|
|
38
|
+
### Option 1: Client Wrapper
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from moss_mcp import wrap_mcp_client
|
|
42
|
+
moss_client = wrap_mcp_client(client, agent_id="my-agent")
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Option 2: MCP Server
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from moss_mcp import MOSSMCPServer
|
|
49
|
+
|
|
50
|
+
server = MOSSMCPServer(agent_id="email-service")
|
|
51
|
+
|
|
52
|
+
@server.tool()
|
|
53
|
+
async def send_email(to: str, body: str) -> str:
|
|
54
|
+
return f"Sent to {to}"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Option 3: Middleware
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from moss_mcp import MOSSMCPMiddleware
|
|
61
|
+
|
|
62
|
+
middleware = MOSSMCPMiddleware(
|
|
63
|
+
agent_id="my-service",
|
|
64
|
+
block_on_policy_violation=True,
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
| Variable | Description | Default |
|
|
71
|
+
|----------|-------------|---------|
|
|
72
|
+
| `MOSS_API_KEY` | Enterprise API key | None (local mode) |
|
|
73
|
+
| `MOSS_API_URL` | API endpoint | `https://api.mosscomputing.com` |
|
|
74
|
+
|
|
75
|
+
## Policy Enforcement
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from moss_mcp.server import PolicyViolationError
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
await moss_client.call_tool("execute_trade", {"amount": 1000000})
|
|
82
|
+
except PolicyViolationError as e:
|
|
83
|
+
print(f"Blocked: {e.reason}")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Causal Chaining
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
result1 = await sign_tool_request_async("analyze_data", {...}, agent_id="agent-1")
|
|
90
|
+
|
|
91
|
+
result2 = await sign_tool_request_async(
|
|
92
|
+
"make_decision",
|
|
93
|
+
{...},
|
|
94
|
+
agent_id="agent-2",
|
|
95
|
+
parent_sig=result1.signature_id
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Links
|
|
100
|
+
|
|
101
|
+
- [Documentation](https://docs.mosscomputing.com/sdks/mcp)
|
|
102
|
+
- [API Reference](https://api.mosscomputing.com/docs)
|
|
103
|
+
- [Dashboard](https://app.mosscomputing.com)
|
|
104
|
+
- [MCP Specification](https://modelcontextprotocol.io)
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MOSS MCP Integration - Protocol-Level Governance for Model Context Protocol
|
|
3
|
+
|
|
4
|
+
This package provides MOSS signing at the MCP transport layer, enabling
|
|
5
|
+
governance of ALL tool calls regardless of the agent framework.
|
|
6
|
+
|
|
7
|
+
MCP (Model Context Protocol) is Anthropic's standard for agent-to-tool
|
|
8
|
+
communication. By intercepting at this layer, MOSS governs every tool
|
|
9
|
+
invocation with cryptographic signatures and policy enforcement.
|
|
10
|
+
|
|
11
|
+
Quick Start:
|
|
12
|
+
from moss_mcp import MOSSMCPServer, wrap_mcp_client
|
|
13
|
+
|
|
14
|
+
# Server-side: Wrap your MCP server
|
|
15
|
+
server = MOSSMCPServer(
|
|
16
|
+
agent_id="my-agent",
|
|
17
|
+
wrapped_server=your_mcp_server,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Client-side: Wrap MCP client calls
|
|
21
|
+
client = wrap_mcp_client(your_mcp_client, agent_id="my-agent")
|
|
22
|
+
result = await client.call_tool("weather", {"city": "SF"})
|
|
23
|
+
# Tool call is signed before execution, result signed after
|
|
24
|
+
|
|
25
|
+
Why MCP-Level Governance:
|
|
26
|
+
- Framework-agnostic: Works with LangChain, LangGraph, AutoGen, any MCP client
|
|
27
|
+
- Protocol-level: Governs ALL tools, not just the ones you remember to wrap
|
|
28
|
+
- Non-bypassable: Agents can't skip signing without losing tool access
|
|
29
|
+
- Future-proof: As MCP becomes standard, MOSS is already there
|
|
30
|
+
|
|
31
|
+
Architecture:
|
|
32
|
+
Agent -> MCP Client -> MOSS Signing -> MCP Server -> Tool
|
|
33
|
+
|
|
|
34
|
+
v
|
|
35
|
+
MOSS API (policies, audit, evidence)
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
__version__ = "0.1.0"
|
|
39
|
+
|
|
40
|
+
from .server import MOSSMCPServer
|
|
41
|
+
from .client import wrap_mcp_client, MOSSMCPClient
|
|
42
|
+
from .signing import (
|
|
43
|
+
sign_tool_request,
|
|
44
|
+
sign_tool_response,
|
|
45
|
+
sign_resource_access,
|
|
46
|
+
verify_signed_request,
|
|
47
|
+
)
|
|
48
|
+
from .middleware import MOSSMCPMiddleware
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"MOSSMCPServer",
|
|
52
|
+
"MOSSMCPClient",
|
|
53
|
+
"wrap_mcp_client",
|
|
54
|
+
"sign_tool_request",
|
|
55
|
+
"sign_tool_response",
|
|
56
|
+
"sign_resource_access",
|
|
57
|
+
"verify_signed_request",
|
|
58
|
+
"MOSSMCPMiddleware",
|
|
59
|
+
]
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MOSS MCP Client Wrapper
|
|
3
|
+
|
|
4
|
+
Wraps MCP client calls to add MOSS signing transparently.
|
|
5
|
+
This is the client-side integration point.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
from typing import Any, Dict, Optional, TypeVar
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
from .signing import (
|
|
14
|
+
sign_tool_request_async,
|
|
15
|
+
sign_tool_response_async,
|
|
16
|
+
MCPSignResult,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
T = TypeVar("T")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class MOSSMCPClientConfig:
|
|
26
|
+
"""Configuration for MOSS MCP Client."""
|
|
27
|
+
agent_id: str
|
|
28
|
+
sign_requests: bool = True
|
|
29
|
+
sign_responses: bool = True
|
|
30
|
+
block_on_policy_violation: bool = True
|
|
31
|
+
context: Dict[str, Any] = None
|
|
32
|
+
|
|
33
|
+
def __post_init__(self):
|
|
34
|
+
if self.context is None:
|
|
35
|
+
self.context = {}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class MOSSMCPClient:
|
|
39
|
+
"""
|
|
40
|
+
MOSS-enabled MCP Client wrapper.
|
|
41
|
+
|
|
42
|
+
Wraps any MCP client to add cryptographic signing to all tool calls.
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
from mcp import Client
|
|
46
|
+
from moss_mcp import MOSSMCPClient
|
|
47
|
+
|
|
48
|
+
# Your existing MCP client
|
|
49
|
+
mcp_client = Client(...)
|
|
50
|
+
|
|
51
|
+
# Wrap with MOSS
|
|
52
|
+
moss_client = MOSSMCPClient(
|
|
53
|
+
agent_id="my-agent",
|
|
54
|
+
wrapped_client=mcp_client,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# All tool calls now go through MOSS
|
|
58
|
+
result = await moss_client.call_tool("weather", {"city": "SF"})
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
agent_id: str,
|
|
64
|
+
wrapped_client: Any,
|
|
65
|
+
*,
|
|
66
|
+
sign_requests: bool = True,
|
|
67
|
+
sign_responses: bool = True,
|
|
68
|
+
block_on_policy_violation: bool = True,
|
|
69
|
+
context: Optional[Dict[str, Any]] = None,
|
|
70
|
+
):
|
|
71
|
+
self.config = MOSSMCPClientConfig(
|
|
72
|
+
agent_id=agent_id,
|
|
73
|
+
sign_requests=sign_requests,
|
|
74
|
+
sign_responses=sign_responses,
|
|
75
|
+
block_on_policy_violation=block_on_policy_violation,
|
|
76
|
+
context=context,
|
|
77
|
+
)
|
|
78
|
+
self.wrapped_client = wrapped_client
|
|
79
|
+
self._signatures: Dict[str, MCPSignResult] = {}
|
|
80
|
+
|
|
81
|
+
async def call_tool(
|
|
82
|
+
self,
|
|
83
|
+
name: str,
|
|
84
|
+
arguments: Dict[str, Any],
|
|
85
|
+
*,
|
|
86
|
+
context: Optional[Dict[str, Any]] = None,
|
|
87
|
+
) -> Any:
|
|
88
|
+
"""
|
|
89
|
+
Call a tool through the wrapped MCP client with MOSS signing.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
name: Tool name
|
|
93
|
+
arguments: Tool arguments
|
|
94
|
+
context: Additional context for signing
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Tool result from the MCP server
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
PolicyViolationError: If MOSS policy blocks the request
|
|
101
|
+
"""
|
|
102
|
+
merged_context = {**self.config.context, **(context or {})}
|
|
103
|
+
|
|
104
|
+
# Step 1: Sign request
|
|
105
|
+
request_sig = None
|
|
106
|
+
if self.config.sign_requests:
|
|
107
|
+
request_result = await sign_tool_request_async(
|
|
108
|
+
tool_name=name,
|
|
109
|
+
arguments=arguments,
|
|
110
|
+
agent_id=self.config.agent_id,
|
|
111
|
+
context=merged_context,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
self._signatures[f"request:{name}:{request_result.signature_id}"] = request_result
|
|
115
|
+
request_sig = request_result.signature_id
|
|
116
|
+
|
|
117
|
+
if request_result.blocked and self.config.block_on_policy_violation:
|
|
118
|
+
from .server import PolicyViolationError
|
|
119
|
+
raise PolicyViolationError(
|
|
120
|
+
tool=name,
|
|
121
|
+
reason=request_result.block_reason or "Policy violation",
|
|
122
|
+
signature_id=request_result.signature_id,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
logger.debug(f"MOSS: Signed tool request {name} -> {request_sig}")
|
|
126
|
+
|
|
127
|
+
# Step 2: Call wrapped client
|
|
128
|
+
result = await self._call_wrapped(name, arguments)
|
|
129
|
+
|
|
130
|
+
# Step 3: Sign response
|
|
131
|
+
if self.config.sign_responses:
|
|
132
|
+
response_result = await sign_tool_response_async(
|
|
133
|
+
tool_name=name,
|
|
134
|
+
result=result,
|
|
135
|
+
agent_id=self.config.agent_id,
|
|
136
|
+
request_sig=request_sig,
|
|
137
|
+
context=merged_context,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
self._signatures[f"response:{name}:{response_result.signature_id}"] = response_result
|
|
141
|
+
logger.debug(f"MOSS: Signed tool response {name} -> {response_result.signature_id}")
|
|
142
|
+
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
async def _call_wrapped(self, name: str, arguments: Dict[str, Any]) -> Any:
|
|
146
|
+
"""Call the wrapped MCP client."""
|
|
147
|
+
if hasattr(self.wrapped_client, "call_tool"):
|
|
148
|
+
method = self.wrapped_client.call_tool
|
|
149
|
+
if asyncio.iscoroutinefunction(method):
|
|
150
|
+
return await method(name, arguments)
|
|
151
|
+
else:
|
|
152
|
+
return method(name, arguments)
|
|
153
|
+
elif hasattr(self.wrapped_client, "tools") and hasattr(self.wrapped_client.tools, "call"):
|
|
154
|
+
# Some MCP clients use client.tools.call()
|
|
155
|
+
method = self.wrapped_client.tools.call
|
|
156
|
+
if asyncio.iscoroutinefunction(method):
|
|
157
|
+
return await method(name, arguments)
|
|
158
|
+
else:
|
|
159
|
+
return method(name, arguments)
|
|
160
|
+
else:
|
|
161
|
+
raise AttributeError(
|
|
162
|
+
f"Wrapped client {type(self.wrapped_client)} does not have call_tool method"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def get_signatures(self) -> Dict[str, MCPSignResult]:
|
|
166
|
+
"""Get all signatures from this session."""
|
|
167
|
+
return self._signatures.copy()
|
|
168
|
+
|
|
169
|
+
def __getattr__(self, name: str) -> Any:
|
|
170
|
+
"""Proxy all other attributes to wrapped client."""
|
|
171
|
+
return getattr(self.wrapped_client, name)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def wrap_mcp_client(
|
|
175
|
+
client: Any,
|
|
176
|
+
agent_id: str,
|
|
177
|
+
*,
|
|
178
|
+
sign_requests: bool = True,
|
|
179
|
+
sign_responses: bool = True,
|
|
180
|
+
block_on_policy_violation: bool = True,
|
|
181
|
+
context: Optional[Dict[str, Any]] = None,
|
|
182
|
+
) -> MOSSMCPClient:
|
|
183
|
+
"""
|
|
184
|
+
Wrap an MCP client with MOSS signing.
|
|
185
|
+
|
|
186
|
+
This is the simplest way to add MOSS governance to an existing MCP client.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
client: Any MCP client instance
|
|
190
|
+
agent_id: Identifier for the agent using this client
|
|
191
|
+
sign_requests: Sign tool requests before execution
|
|
192
|
+
sign_responses: Sign tool responses after execution
|
|
193
|
+
block_on_policy_violation: Raise error if policy blocks
|
|
194
|
+
context: Default context for all calls
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
MOSSMCPClient wrapper
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
from mcp import Client
|
|
201
|
+
from moss_mcp import wrap_mcp_client
|
|
202
|
+
|
|
203
|
+
client = Client(...)
|
|
204
|
+
moss_client = wrap_mcp_client(client, agent_id="my-agent")
|
|
205
|
+
|
|
206
|
+
# Use moss_client.call_tool() - all calls are signed
|
|
207
|
+
result = await moss_client.call_tool("weather", {"city": "SF"})
|
|
208
|
+
"""
|
|
209
|
+
return MOSSMCPClient(
|
|
210
|
+
agent_id=agent_id,
|
|
211
|
+
wrapped_client=client,
|
|
212
|
+
sign_requests=sign_requests,
|
|
213
|
+
sign_responses=sign_responses,
|
|
214
|
+
block_on_policy_violation=block_on_policy_violation,
|
|
215
|
+
context=context,
|
|
216
|
+
)
|