Skip to content

Commit c0fd5bd

Browse files
authored
Add a composite tools demo vMCP with arXiv (#31)
Signed-off-by: Dan Barr <[email protected]> Co-authored-by: Dan Barr <[email protected]>
1 parent d42a450 commit c0fd5bd

4 files changed

Lines changed: 136 additions & 1 deletion

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ The goal is a fully functional ToolHive platform running locally to exercise all
88

99
So far, it includes:
1010

11-
- A ToolHive Registry Server with a few servers filtered from the main ToolHive registry and with auto-discovery enabled
11+
- A ToolHive Registry Server with a few servers filtered from the official and ToolHive registries plus K8s discovery
1212
- The ToolHive Cloud UI connected to the registry server with a mock OIDC provider for authentication
1313
- A Virtual MCP Server running a few basic MCP servers: fetch, osv, oci-registry, and context7
14+
- A vMCP server with a composite tool chaining together multiple arXiv tools
1415
- The MKP MCP server for managing the cluster, exposed directly
1516
- An MCP Optimizer server for intelligent tool calling across multiple MCP servers
1617
- Traefik as the gateway for routing traffic into the cluster
@@ -101,6 +102,7 @@ Bootstrap complete! Access your demo services at the following URLs:
101102
(run 'thv config set-registry http://registry-172-19-0-3.traefik.me/registry --allow-private-ip' to configure ToolHive to use it)
102103
- MKP MCP server at http://mcp-172-19-0-3.traefik.me/mkp/mcp
103104
- vMCP demo server at http://mcp-172-19-0-3.traefik.me/vmcp-demo/mcp
105+
- vMCP composite tool demo server at http://mcp-172-19-0-3.traefik.me/vmcp-research/mcp
104106
- MCP Optimizer at http://mcp-172-19-0-3.traefik.me/mcp-optimizer/mcp
105107
- Grafana at http://grafana-172-19-0-3.traefik.me
106108
```

bootstrap.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ run_quiet kubectl apply -f demo-manifests/vmcp-mcpservers.yaml || die "Failed to
175175
# Wait for vMCP backend MCPServer resources to reach Running phase
176176
run_quiet kubectl wait --for=jsonpath='{.status.phase}'=Running --timeout=300s mcpserver -l demo.toolhive.stacklok.dev/vmcp-backend=true -n toolhive-system || die "vMCP backend MCPServer resources failed to become ready"
177177
run_quiet sh -c "envsubst < demo-manifests/vmcp-demo-simple.yaml | kubectl apply -f -" || die "Failed to apply vMCP demo"
178+
run_quiet sh -c "envsubst < demo-manifests/vmcp-demo-composite.yaml | kubectl apply -f -" || die "Failed to apply vMCP composite tools demo"
178179
# run_quiet sh -c "envsubst < demo-manifests/vmcp-demo-auth.yaml | kubectl apply -f -" || die "Failed to apply vMCP demo with auth"
179180
echo ""
180181

@@ -225,6 +226,13 @@ cat > demo-endpoints.json <<EOF
225226
"test_with_thv": true,
226227
"healthcheck_path": "/vmcp-demo/health"
227228
},
229+
{
230+
"name": "vMCP Composite Tool Demo Server",
231+
"url": "http://$MCP_HOSTNAME/vmcp-research/mcp",
232+
"type": "mcp",
233+
"test_with_thv": true,
234+
"healthcheck_path": "/vmcp-research/health"
235+
},
228236
{
229237
"name": "MCP Optimizer",
230238
"url": "http://$MCP_HOSTNAME/mcp-optimizer/mcp",
@@ -249,5 +257,6 @@ echo " - ToolHive Registry Server at http://$REGISTRY_HOSTNAME/registry"
249257
echo " (run 'thv config set-registry http://$REGISTRY_HOSTNAME/registry --allow-private-ip' to configure ToolHive to use it)"
250258
echo " - MKP MCP server at http://$MCP_HOSTNAME/mkp/mcp"
251259
echo " - vMCP demo server at http://$MCP_HOSTNAME/vmcp-demo/mcp"
260+
echo " - vMCP composite tool demo server at http://$MCP_HOSTNAME/vmcp-research/mcp"
252261
echo " - MCP Optimizer at http://$MCP_HOSTNAME/mcp-optimizer/mcp"
253262
echo " - Grafana at http://$GRAFANA_HOSTNAME"
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
apiVersion: toolhive.stacklok.dev/v1alpha1
2+
kind: VirtualMCPServer
3+
metadata:
4+
name: vmcp-research
5+
namespace: toolhive-system
6+
annotations:
7+
toolhive.stacklok.dev/registry-export: "true"
8+
toolhive.stacklok.dev/registry-url: "http://$MCP_HOSTNAME/vmcp-research/mcp"
9+
toolhive.stacklok.dev/registry-description: |
10+
A vMCP server demonstrating a research workflow with a composite tool.
11+
spec:
12+
config:
13+
groupRef: research-tools
14+
telemetry:
15+
endpoint: mcp-collector.observability:4318
16+
insecure: true
17+
serviceName: vmcp-research-server
18+
serviceVersion: "1.0.0"
19+
metricsEnabled: true
20+
tracingEnabled: true
21+
samplingRate: "1.0"
22+
aggregation:
23+
conflictResolution: prefix
24+
conflictResolutionConfig:
25+
prefixFormat: "{workload}_"
26+
tools:
27+
- workload: arxiv
28+
filter:
29+
- "list_papers"
30+
compositeTools:
31+
- name: research_topic
32+
description: Search arXiv for papers and read the top result
33+
parameters:
34+
type: object
35+
properties:
36+
query:
37+
type: string
38+
description: Research topic to search for
39+
required:
40+
- query
41+
steps:
42+
# Step 1: Search arXiv for papers matching the query
43+
- id: search
44+
tool: arxiv_search_papers
45+
arguments:
46+
query: "{{.params.query}}"
47+
max_results: 1
48+
# Step 2: Download the paper (required before reading)
49+
# Note: fromJson is needed when the MCP server returns JSON as text
50+
# rather than structured content. This is common for servers that
51+
# don't fully support MCP's structuredContent field.
52+
- id: download
53+
tool: arxiv_download_paper
54+
arguments:
55+
paper_id: "{{(index (fromJson .steps.search.output.text).papers 0).id}}"
56+
dependsOn: [search]
57+
# Step 3: Read the downloaded paper content
58+
- id: read
59+
tool: arxiv_read_paper
60+
arguments:
61+
paper_id: "{{(index (fromJson .steps.search.output.text).papers 0).id}}"
62+
dependsOn: [download]
63+
incomingAuth:
64+
type: anonymous
65+
serviceType: ClusterIP
66+
---
67+
apiVersion: gateway.networking.k8s.io/v1
68+
kind: HTTPRoute
69+
metadata:
70+
name: vmcp-research-route
71+
namespace: toolhive-system
72+
spec:
73+
parentRefs:
74+
- name: traefik-gateway
75+
namespace: traefik
76+
rules:
77+
- matches:
78+
- path:
79+
type: PathPrefix
80+
value: /vmcp-research
81+
filters:
82+
- type: URLRewrite
83+
urlRewrite:
84+
path:
85+
type: ReplacePrefixMatch
86+
replacePrefixMatch: /
87+
backendRefs:
88+
- name: vmcp-vmcp-research
89+
port: 4483

demo-manifests/vmcp-mcpservers.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,38 @@ spec:
6161
transport: stdio
6262
proxyPort: 8080
6363
proxyMode: streamable-http
64+
---
65+
apiVersion: toolhive.stacklok.dev/v1alpha1
66+
kind: MCPGroup
67+
metadata:
68+
name: research-tools
69+
namespace: toolhive-system
70+
spec:
71+
description: Group for vMCP research workflow tools
72+
---
73+
apiVersion: toolhive.stacklok.dev/v1alpha1
74+
kind: MCPServer
75+
metadata:
76+
name: arxiv
77+
namespace: toolhive-system
78+
labels:
79+
demo.toolhive.stacklok.dev/vmcp-backend: "true"
80+
spec:
81+
image: ghcr.io/stacklok/dockyard/uvx/arxiv-mcp-server:0.3.2
82+
groupRef: research-tools
83+
transport: stdio
84+
proxyPort: 8080
85+
proxyMode: streamable-http
86+
args:
87+
- --storage-path
88+
- /arxiv-papers
89+
podTemplateSpec:
90+
spec:
91+
volumes:
92+
- name: arxiv-papers
93+
emptyDir: {}
94+
containers:
95+
- name: mcp
96+
volumeMounts:
97+
- name: arxiv-papers
98+
mountPath: /arxiv-papers

0 commit comments

Comments
 (0)